From 632f62265b722de6f519cb4abdf2847c60a1e68b Mon Sep 17 00:00:00 2001 From: Dmitry Puzyrkov <dmitry.puzyrkov@demlabs.net> Date: Mon, 8 Jul 2024 13:05:12 +0000 Subject: [PATCH] Feature conftool servicectl --- conftool/CMakeLists.txt | 6 +- conftool/commands/abstractcommand.cpp | 4 +- conftool/commands/conditioncommand.cpp | 1 - conftool/commands/servicecommand.cpp | 87 +++++++++++++ conftool/commands/servicecommand.h | 11 ++ conftool/commands/storagecommand.cpp | 1 + conftool/main.cpp | 164 ++++++++++++++----------- conftool/service/service.h | 25 ++++ conftool/service/service_linux.cpp | 54 ++++++++ conftool/service/service_macos.cpp | 44 +++++++ conftool/service/service_win.cpp | 81 ++++++++++++ dist/share/default.setup | 12 +- os/debian/postinst | 7 +- os/macos/PKGINSTALL/postinstall | 6 +- os/windows/cellframe-node.nsis | 7 +- 15 files changed, 430 insertions(+), 80 deletions(-) create mode 100644 conftool/commands/servicecommand.cpp create mode 100644 conftool/commands/servicecommand.h create mode 100644 conftool/service/service.h create mode 100644 conftool/service/service_linux.cpp create mode 100644 conftool/service/service_macos.cpp create mode 100644 conftool/service/service_win.cpp diff --git a/conftool/CMakeLists.txt b/conftool/CMakeLists.txt index 8c53fdd2d..c7079a567 100644 --- a/conftool/CMakeLists.txt +++ b/conftool/CMakeLists.txt @@ -31,8 +31,12 @@ add_executable(${PROJECT_NAME} ./main.cpp ./commands/conditioncommand.cpp ./commands/storagecommand.cpp ./commands/configcommand.cpp + ./commands/servicecommand.cpp ./commands/fromtemplatecommand.cpp - ./config/cellframeconfigfile.cpp) + ./config/cellframeconfigfile.cpp + ./service/service_win.cpp + ./service/service_linux.cpp + ./service/service_macos.cpp) target_link_libraries(cellframe-node-config PRIVATE ftxui::screen diff --git a/conftool/commands/abstractcommand.cpp b/conftool/commands/abstractcommand.cpp index 9cd8bc7b5..fe79b8315 100644 --- a/conftool/commands/abstractcommand.cpp +++ b/conftool/commands/abstractcommand.cpp @@ -57,4 +57,6 @@ bool CAbstractScriptCommand::is_condition_open(){ } bool CAbstractScriptCommand::is_condition_close(){ return false; -} \ No newline at end of file +} + +std::map<std::string, std::string> variable_storage; \ No newline at end of file diff --git a/conftool/commands/conditioncommand.cpp b/conftool/commands/conditioncommand.cpp index 2f62ae742..d02ea1f33 100644 --- a/conftool/commands/conditioncommand.cpp +++ b/conftool/commands/conditioncommand.cpp @@ -91,4 +91,3 @@ bool CConditionCloseCommand::execute(bool non_intercative, int flags) { return true; } -std::map<std::string, std::string> variable_storage; \ No newline at end of file diff --git a/conftool/commands/servicecommand.cpp b/conftool/commands/servicecommand.cpp new file mode 100644 index 000000000..c94bad005 --- /dev/null +++ b/conftool/commands/servicecommand.cpp @@ -0,0 +1,87 @@ +#include "servicecommand.h" +#include <stdexcept> +#include <filesystem> + +#include "../build_config.h" +#include "../config/cellframeconfigfile.h" +#include "../service/service.h" + +namespace fs = std::filesystem; + +CAbstractScriptCommand::Registrar<CServiceCommand> service_registrar("service"); + + +CServiceCommand::CServiceCommand(std::vector <std::string> cmd_tokens): CAbstractScriptCommand(cmd_tokens) +{ + //zero token is always a command (network) + + if (cmd_tokens.size() < 2) + throw std::invalid_argument("service command require action argument"); + + this->action = cmd_tokens[1]; +} + +bool CServiceCommand::execute(bool non_intercative, int flags) +{ + if (this->action == "enable"){ + if (CServiceControl::enable()) { + std::cout << "enabled" <<std::endl; + } + else{ + std::cout << "error" <<std::endl; + } + } + + if (this->action == "disable") + { + if (CServiceControl::disable()) { + std::cout << "disabled" <<std::endl; + } + else{ + std::cout << "error" <<std::endl; + } + } + + if (this->action == "status") + { + std::cout << (CServiceControl::serviceStatus() == ENABLED?"enabled":"disabled")<<std::endl; + } + + if (this->action == "start") + { + if (CServiceControl::start()) + { + std::cout << "started" << std::endl; + } + else + { + std::cout << "error" << std::endl; + } + } + + if (this->action == "stop") + { + if (CServiceControl::stop()) + { + std::cout << "stoped" << std::endl; + } + else + { + std::cout << "error" << std::endl; + } + } + + if (this->action == "restart") + { + if (CServiceControl::restart()) + { + std::cout << "restarted" << std::endl; + } + else + { + std::cout << "error" << std::endl; + } + } + + return true; +} diff --git a/conftool/commands/servicecommand.h b/conftool/commands/servicecommand.h new file mode 100644 index 000000000..a0b2f47a2 --- /dev/null +++ b/conftool/commands/servicecommand.h @@ -0,0 +1,11 @@ +#include "abstractcommand.h" + +class CServiceCommand : public CAbstractScriptCommand { + public: + CServiceCommand(std::vector <std::string> cmd_tokens); + static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CServiceCommand>(cmd_tokens); } + bool execute(bool non_interactive, int flags); + + private: + std::string action; +}; diff --git a/conftool/commands/storagecommand.cpp b/conftool/commands/storagecommand.cpp index e16c68bbb..b02a9f3c4 100644 --- a/conftool/commands/storagecommand.cpp +++ b/conftool/commands/storagecommand.cpp @@ -31,3 +31,4 @@ bool CVariableCommand::execute(bool non_intercative, int flags) } return true; } + diff --git a/conftool/main.cpp b/conftool/main.cpp index 14df00e18..aa17d93d7 100644 --- a/conftool/main.cpp +++ b/conftool/main.cpp @@ -54,8 +54,101 @@ std::string getHostName(void) return res; } +#ifdef WIN32 + +LONG GetDWORDRegKey(HKEY hKey, const std::wstring &strValueName, DWORD &nValue, DWORD nDefaultValue) +{ + nValue = nDefaultValue; + DWORD dwBufferSize(sizeof(DWORD)); + DWORD nResult(0); + LONG nError = ::RegQueryValueExW(hKey, + strValueName.c_str(), + 0, + NULL, + reinterpret_cast<LPBYTE>(&nResult), + &dwBufferSize); + if (ERROR_SUCCESS == nError) + { + nValue = nResult; + } + return nError; +} + + +LONG GetBoolRegKey(HKEY hKey, const std::wstring &strValueName, bool &bValue, bool bDefaultValue) +{ + DWORD nDefValue((bDefaultValue) ? 1 : 0); + DWORD nResult(nDefValue); + LONG nError = GetDWORDRegKey(hKey, strValueName.c_str(), nResult, nDefValue); + if (ERROR_SUCCESS == nError) + { + bValue = (nResult != 0) ? true : false; + } + return nError; +} + + +LONG GetStringRegKey(HKEY hKey, const std::wstring &strValueName, std::wstring &strValue, const std::wstring &strDefaultValue) +{ + strValue = strDefaultValue; + WCHAR szBuffer[512]; + DWORD dwBufferSize = sizeof(szBuffer); + ULONG nError; + nError = RegQueryValueExW(hKey, strValueName.c_str(), 0, NULL, (LPBYTE)szBuffer, &dwBufferSize); + if (ERROR_SUCCESS == nError) + { + strValue = szBuffer; + } + return nError; +} + +#endif + namespace fs = std::filesystem; +std::string getNodeConfigPath(){ + #ifdef __linux__ + return "/opt/cellframe-node/"; + #endif + + #ifdef __APPLE__ + return "/Applications/CellframeNode.app/Contents/Resources/"; + #endif + + #ifdef WIN32 + HKEY hKey; + LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_READ, &hKey); + bool bExistsAndSuccess (lRes == ERROR_SUCCESS); + bool bDoesNotExistsSpecifically (lRes == ERROR_FILE_NOT_FOUND); + std::wstring path; + GetStringRegKey(hKey, L"Common Documents", path, L""); + std::string stdpath(path.begin(),path.end()); + return (std::filesystem::path{stdpath}/"cellframe-node/").string(); + #endif +} + +std::string getNodeBinaryPath(){ + #ifdef __linux__ + return "/opt/cellframe-node/bin/"; + #endif + + #ifdef __APPLE__ + return "/Applications/CellframeNode.app/Contents/MacOS/"; + #endif + + #ifdef WIN32 + //HKLM "Software\${APP_NAME}" "Path" + HKEY hKey; + LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\cellframe-node\\", 0, KEY_READ, &hKey); + bool bExistsAndSuccess (lRes == ERROR_SUCCESS); + bool bDoesNotExistsSpecifically (lRes == ERROR_FILE_NOT_FOUND); + std::wstring path; + GetStringRegKey(hKey, L"Path", path, L""); + std::string stdpath(path.begin(),path.end()); + return (std::filesystem::path{stdpath}).string(); + #endif +} + bool cmdOptionExists(char** begin, char** end, const std::string& option) { return std::find(begin, end, option) != end; @@ -155,80 +248,13 @@ bool run_commands(std::vector <std::unique_ptr<CAbstractScriptCommand>> &command return true; } -#ifdef WIN32 - -LONG GetDWORDRegKey(HKEY hKey, const std::wstring &strValueName, DWORD &nValue, DWORD nDefaultValue) -{ - nValue = nDefaultValue; - DWORD dwBufferSize(sizeof(DWORD)); - DWORD nResult(0); - LONG nError = ::RegQueryValueExW(hKey, - strValueName.c_str(), - 0, - NULL, - reinterpret_cast<LPBYTE>(&nResult), - &dwBufferSize); - if (ERROR_SUCCESS == nError) - { - nValue = nResult; - } - return nError; -} - - -LONG GetBoolRegKey(HKEY hKey, const std::wstring &strValueName, bool &bValue, bool bDefaultValue) -{ - DWORD nDefValue((bDefaultValue) ? 1 : 0); - DWORD nResult(nDefValue); - LONG nError = GetDWORDRegKey(hKey, strValueName.c_str(), nResult, nDefValue); - if (ERROR_SUCCESS == nError) - { - bValue = (nResult != 0) ? true : false; - } - return nError; -} - - -LONG GetStringRegKey(HKEY hKey, const std::wstring &strValueName, std::wstring &strValue, const std::wstring &strDefaultValue) -{ - strValue = strDefaultValue; - WCHAR szBuffer[512]; - DWORD dwBufferSize = sizeof(szBuffer); - ULONG nError; - nError = RegQueryValueExW(hKey, strValueName.c_str(), 0, NULL, (LPBYTE)szBuffer, &dwBufferSize); - if (ERROR_SUCCESS == nError) - { - strValue = szBuffer; - } - return nError; -} - -#endif void populate_variables() { variable_storage["HOST_OS"] = HOST_OS; variable_storage["HOSTNAME"] = getHostName(); - - #ifdef __linux__ - variable_storage["CONFIGS_PATH"] = "/opt/cellframe-node/"; - #endif - - #ifdef WIN32 - HKEY hKey; - LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_READ, &hKey); - bool bExistsAndSuccess (lRes == ERROR_SUCCESS); - bool bDoesNotExistsSpecifically (lRes == ERROR_FILE_NOT_FOUND); - std::wstring path; - GetStringRegKey(hKey, L"Common Documents", path, L""); - std::string stdpath(path.begin(),path.end()); - variable_storage["CONFIGS_PATH"] = (std::filesystem::path{stdpath}/"cellframe-node/").string(); - #endif - - #ifdef __APPLE__ - variable_storage["CONFIGS_PATH"] = "/Applications/CellframeNode.app/Contents/Resources/"; - #endif - + variable_storage["CONFIGS_PATH"] = getNodeConfigPath(); + variable_storage["NODE_BINARY_PATH"] = getNodeBinaryPath(); } int init_configs(int argc, char *argv[], int flags) diff --git a/conftool/service/service.h b/conftool/service/service.h new file mode 100644 index 000000000..ecc7fba61 --- /dev/null +++ b/conftool/service/service.h @@ -0,0 +1,25 @@ +#include <algorithm> +#include <map> +#include <string> +#include <iostream> +#include <cstdint> +#include <filesystem> +#include <fstream> +#include <iostream> +#include <vector> +#include <stdexcept> + +enum EServiceStatus{ + ENABLED, + DISABLED, +}; + +struct CServiceControl +{ + static bool enable(); + static bool disable(); + static EServiceStatus serviceStatus(); + static bool start(); + static bool stop(); + static bool restart(); +}; \ No newline at end of file diff --git a/conftool/service/service_linux.cpp b/conftool/service/service_linux.cpp new file mode 100644 index 000000000..53107e64b --- /dev/null +++ b/conftool/service/service_linux.cpp @@ -0,0 +1,54 @@ + #ifdef __linux__ + +#include "service.h" +#include "../commands/abstractcommand.h" + +bool CServiceControl::enable(){ + std::string cmd = "systemctl enable " + (std::filesystem::path{variable_storage["CONFIGS_PATH"]}/"share"/"cellframe-node.service").string(); + int res = std::system(cmd.c_str()); + + return res == 0 ? true : false; +} + +bool CServiceControl::disable() +{ + std::string cmd = "systemctl disable cellframe-node.service"; + int res = std::system(cmd.c_str()); + return res == 0 ? true : false; +} + +EServiceStatus CServiceControl::serviceStatus() +{ + std::string cmd = "systemctl is-enabled cellframe-node.service"; + int res = std::system(cmd.c_str()); + switch (res) + { + case 0: + return ENABLED; + default: + return DISABLED; + } +} + +bool CServiceControl::start() +{ + std::string cmd = "systemctl start cellframe-node.service"; + int res = std::system(cmd.c_str()); + return res == 0 ? true : false; +} + +bool CServiceControl::stop() +{ + std::string cmd = "systemctl stop cellframe-node.service"; + int res = std::system(cmd.c_str()); + return res == 0 ? true : false; +} + +bool CServiceControl::restart() +{ + std::string cmd = "systemctl restart cellframe-node.service"; + int res = std::system(cmd.c_str()); + return res == 0 ? true : false; +} + + #endif \ No newline at end of file diff --git a/conftool/service/service_macos.cpp b/conftool/service/service_macos.cpp new file mode 100644 index 000000000..ed1f0c55c --- /dev/null +++ b/conftool/service/service_macos.cpp @@ -0,0 +1,44 @@ +#ifdef __APPLE__ + +#include "service.h" +#include "../commands/abstractcommand.h" + +bool CServiceControl::enable() +{ + //starts too + int res = std::system("launchctl load -w /Library/LaunchDaemons/com.demlabs.cellframe-node.plist"); + return res == 0 ? true : false; +} + +bool CServiceControl::disable() +{ + //stops too + int res = std::system("launchctl unload -w /Library/LaunchDaemons/com.demlabs.cellframe-node.plist"); + return res == 0 ? true : false; +} + +EServiceStatus CServiceControl::serviceStatus() +{ + std::string cmd = std::string(); + int res = std::system("launchctl list com.demlabs.cellframe-node"); + return res == 0 ? ENABLED : DISABLED; +} + +bool CServiceControl::start() +{ + return enable(); +} + +bool CServiceControl::stop() +{ + return disable(); +} + +bool CServiceControl::restart() +{ + stop(); + start(); + return true; +} + +#endif \ No newline at end of file diff --git a/conftool/service/service_win.cpp b/conftool/service/service_win.cpp new file mode 100644 index 000000000..2a6606c7c --- /dev/null +++ b/conftool/service/service_win.cpp @@ -0,0 +1,81 @@ + #ifdef WIN32 + +#include "service.h" +#include "../commands/abstractcommand.h" +#include <windows.h> +#include <windowsx.h> +#include <shlobj.h> + + +int runShellAdmin(std::string app, std::string cmd) +{ + // Launch itself as admin + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.lpVerb = "runas"; + sei.lpFile = app.c_str(); + sei.lpParameters = cmd.c_str(); + sei.hwnd = NULL; + sei.nShow = SW_HIDE; + + if (!ShellExecuteEx(&sei)) + { + DWORD dwError = GetLastError(); + if (dwError == ERROR_CANCELLED) + { + std::cout << "End user did not allow elevation" << std::endl; + return -1; + } + + std::cout << "Exec failed" << dwError<< std::endl; + + } + WaitForSingleObject(sei.hProcess,INFINITE); + long unsigned int rc; + GetExitCodeProcess(sei.hProcess, &rc); + return rc; +} + + +bool CServiceControl::enable() +{ + auto nodebinpath = std::filesystem::path{variable_storage["NODE_BINARY_PATH"]}/"cellframe-node.exe"; + std::string cmd = std::string("/Create /F /RL highest /SC onlogon /TR \"'") + nodebinpath.string() + "'\" /TN CellframeNode"; + long unsigned int res = runShellAdmin("schtasks.exe", cmd); + return res == 0 ? true : false; +} + +bool CServiceControl::disable() +{ + std::string cmd = std::string("/Delete /TN CellframeNode /f"); + long unsigned int res = runShellAdmin("schtasks.exe", cmd); + return res == 0 ? true : false; +} + +EServiceStatus CServiceControl::serviceStatus() +{ + std::string cmd = std::string("schtasks /query /TN CellframeNode"); + int res = std::system(cmd.c_str()); + return res==0 ? ENABLED : DISABLED; +} + +bool CServiceControl::start() +{ + std::string cmd = std::string("/run /TN CellframeNode"); + long unsigned int res = runShellAdmin("schtasks.exe", cmd); + return res==0 ? true : false; +} + +bool CServiceControl::stop() +{ + std::string cmd = std::string("/end /TN CellframeNode"); + long unsigned int res = runShellAdmin("schtasks.exe", cmd); + return res==0 ? true : false; +} + +bool CServiceControl::restart() +{ + stop(); + start(); + return true; +} + #endif \ No newline at end of file diff --git a/dist/share/default.setup b/dist/share/default.setup index f4ab05600..1e62e013d 100644 --- a/dist/share/default.setup +++ b/dist/share/default.setup @@ -41,7 +41,14 @@ # if ${VAR1}==${VAR2} # #do somthin # endif - +# +# service enable: enable cellframe-node as system-service +# service disable: disable cellframe-node as system-service +# service status: get status cellframe-node as system-service +# service start: run cellframe-node as system-service +# service stop: stop cellframe-node as system-service +# service restart #stop + start +# # Setup Script @@ -88,5 +95,4 @@ config KelVPN general node-role default full config raiden general node-role default full config riemann general node-role default full config mileena general node-role default full -config subzero general node-role default full - +config subzero general node-role default full \ No newline at end of file diff --git a/os/debian/postinst b/os/debian/postinst index dbcaf2c4f..5679349c8 100755 --- a/os/debian/postinst +++ b/os/debian/postinst @@ -33,9 +33,8 @@ chmod 666 $(find ${DAP_PREFIX}/etc/ -type f) #set rwx permissions to dirs chmod 777 $(find ${DAP_PREFIX}/etc/ -type d) -echo "[!] Starting up cellframe-node" -systemctl enable $DAP_PREFIX/share/$DAP_APP_NAME.service || true -systemctl start cellframe-node || true +echo "[!] Setting up cellframe-node as service" +$DAP_PREFIX/bin/cellframe-node-config -e service enable if [ -e "$DAP_PREFIX/bin/cellframe-diagtool" ]; then echo "[!] Starting up cellframe-diagtool" @@ -51,5 +50,7 @@ if [ -e "$DAP_PREFIX/share/cellframe-updater.service" ]; then systemctl start cellframe-updater.timer || true fi +echo "[!] Starting up cellframe-node" +$DAP_PREFIX/bin/cellframe-node-config -e service start echo "[!] Done" diff --git a/os/macos/PKGINSTALL/postinstall b/os/macos/PKGINSTALL/postinstall index 75dc04e60..df2a498e9 100755 --- a/os/macos/PKGINSTALL/postinstall +++ b/os/macos/PKGINSTALL/postinstall @@ -17,4 +17,8 @@ echo "[!] Set cfg permissions" #set rwo permissions to configs chmod 666 $(find ${DAP_PREFIX}/ -type f) #set rwx permissions to dirs -chmod 777 $(find ${DAP_PREFIX}/ -type d) \ No newline at end of file +chmod 777 $(find ${DAP_PREFIX}/ -type d) + +echo "[!] Copy daemon plist" +cp /Applications/CellframeNode.app/Contents/Resources/com.demlabs.cellframe-node.plist /Library/LaunchDaemons/ +/Applications/CellframeNode.app/Contents/MacOS/cellframe-node-config -e service enable diff --git a/os/windows/cellframe-node.nsis b/os/windows/cellframe-node.nsis index 8c16ebc33..ec7ffe39e 100644 --- a/os/windows/cellframe-node.nsis +++ b/os/windows/cellframe-node.nsis @@ -103,6 +103,9 @@ FunctionEnd !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES Page custom pgNetSelectCreate pgNetSelectLeave + + +!define MUI_FINISHPAGE_RUN "$INSTDIR\cellframe-node.exe" !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM @@ -127,6 +130,7 @@ InstallDir "$PROGRAMFILES64\${APP_NAME}" !define PRODUCT_UNINSTALL_EXE "uninstall.exe" + Var NetworksDialog Var NetBackboneCb Var NetKelVPNCb @@ -223,7 +227,7 @@ Section "${APP_NAME}" CORE WriteRegStr HKLM "Software\${APP_NAME}" "Version" "${APP_VERSION}" nsExec::ExecToLog /OEM "$INSTDIR\${NODE_NAME}-config.exe -i $ConfigPath\share\default.setup" - + nsExec::ExecToLog /OEM "$INSTDIR\${NODE_NAME}-config.exe -e service enable" ; check net states after install to show in checkboxes Call getNetworksStates @@ -324,6 +328,7 @@ FunctionEnd Section "Uninstall" SetRegView 64 + nsExec::ExecToLog /OEM "$INSTDIR\${NODE_NAME}-config.exe -e service disable" Delete "$INSTDIR\${NODE_NAME}.exe" Delete "$INSTDIR\${NODE_NAME}-tool.exe" Delete "$INSTDIR\${NODE_NAME}-cli.exe" -- GitLab