Commit 372aa370 authored by root's avatar root
Browse files

Merge branch 'master' of https://github.com/tribe29/checkmk

parents 141029b8 dd940bfe
Title: Fix agent proxmox bug if duration is more than 24 hours
Class: fix
Compatible: compat
Component: checks
Date: 1653323709
Edition: cee
Knowledge: doc
Level: 1
Version: 2.2.0i1
Fix a bug if duration string is more than 24 hours
CMK-9146
In a rare occasion if the backup takes more than 24
hours the standard datetime string parsing fails.
So the manual parsing and feeding bits to timedelta object
solves the problem
......@@ -20,8 +20,16 @@
namespace cma {
// set only when executable works as a service
bool IsService();
bool IsTest();
enum class Modus {
app,
service,
test,
integration,
};
Modus GetModus();
namespace details {
void SetModus(Modus m);
}
} // namespace cma
namespace XLOG {
......
......@@ -2512,27 +2512,30 @@ std::wstring GenerateRandomString(size_t max_length) noexcept {
return ret;
}
static std::wstring CmaUserPrefix() noexcept {
if (cma::IsService()) {
return L"cmk_in_";
}
if (cma::IsTest()) {
return L"cmk_TST_";
namespace {
std::wstring CmaUserPrefix() noexcept {
switch (cma::GetModus()) {
case cma::Modus::service:
return L"cmk_in_";
case cma::Modus::test:
return L"cmk_TST_";
case cma::Modus::integration:
return L"cmk_IT_";
case cma::Modus::app:
return {};
}
// unreachable
return {};
}
} // namespace
std::wstring GenerateCmaUserNameInGroup(std::wstring_view group) noexcept {
if (group.empty()) return {};
if (cma::IsService() || cma::IsTest()) {
auto prefix = CmaUserPrefix();
if (prefix.empty()) return {};
return prefix + group.data();
if (group.empty()) {
return {};
}
return {};
auto prefix = CmaUserPrefix();
return prefix.empty() ? std::wstring{} : prefix + group.data();
}
InternalUser CreateCmaUserInGroup(const std::wstring &group) noexcept {
......@@ -2594,13 +2597,14 @@ void ProtectPathFromUserWrite(const fs::path &path,
void ProtectFileFromUserWrite(const fs::path &path,
std::vector<std::wstring> &commands) {
// CONTEXT: to prevent malicious file creation or modification in folder
// "programdata/checkmk" we must remove inherited write rights for
// Users in checkmk root data folder.
// CONTEXT: to prevent malicious file creation or modification in
// folder "programdata/checkmk" we must remove inherited write rights
// for Users in checkmk root data folder.
constexpr std::wstring_view command_templates[] = {
L"icacls \"{}\" /inheritance:d /c", // disable inheritance
L"icacls \"{}\" /remove:g *S-1-5-32-545 /c", // remove all user rights
L"icacls \"{}\" /remove:g *S-1-5-32-545 /c", // remove all user
// rights
L"icacls \"{}\" /grant:r *S-1-5-32-545:(RX) /c"}; // read/exec
for (auto const t : command_templates) {
......@@ -2615,7 +2619,8 @@ void ProtectPathFromUserAccess(const fs::path &entry,
// CONTEXT: some files must be protected from the user fully
constexpr std::wstring_view command_templates[] = {
L"icacls \"{}\" /inheritance:d /c", // disable inheritance
L"icacls \"{}\" /remove:g *S-1-5-32-545 /c" // remove all user rights
L"icacls \"{}\" /remove:g *S-1-5-32-545 /c" // remove all user
// rights
};
for (auto const t : command_templates) {
......
......@@ -8,13 +8,16 @@
#include <filesystem>
#include <iosfwd>
#include <iostream>
#include <ranges>
#include "cfg.h"
#include "common/cfg_info.h"
#include "common/cfg_yaml.h"
#include "common/cma_yml.h"
#include "common/wtools.h"
namespace fs = std::filesystem;
namespace rs = std::ranges;
using namespace std::chrono_literals;
using namespace std::string_literals;
......@@ -216,10 +219,18 @@ std::wstring BuildCommandLine(const fs::path &controller) {
agent_channel // --channel 50001
));
}
namespace {
std::vector<Modus> start_controller_moduses{Modus::service, Modus::integration};
bool AllowUseController() {
return rs::find(start_controller_moduses, GetModus()) !=
start_controller_moduses.end();
}
} // namespace
std::optional<uint32_t> StartAgentController(const fs::path &service) {
XLOG::l.i("starting controller");
if (!cma::IsService()) {
if (!AllowUseController()) {
return {};
}
......@@ -286,7 +297,7 @@ std::string DetermineAgentCtlStatus() {
}
bool KillAgentController(const fs::path &service) {
if (cma::IsService()) {
if (AllowUseController()) {
auto ret = wtools::KillProcess(cfg::files::kAgentCtl, 1);
// Idiotic loop below mirrors idiotic Windows architecture.
// MS: Even if process killed, the executable may be for some time busy.
......
......@@ -54,14 +54,14 @@ bool RemoveNode(const std::string &name) {
namespace cma {
namespace details {
// internal and hidden global variables
// #GLOBAL x2
bool g_is_service = false; // set to true only when we run service
bool g_is_test = false; // set to true only when we run watest
Modus g_modus{Modus::app};
void SetModus(Modus m) {
XLOG::d.i("change modus to {}", static_cast<uint32_t>(m));
g_modus = m;
}
} // namespace details
bool IsService() { return details::g_is_service; }
bool IsTest() { return details::g_is_test; }
Modus GetModus() { return details::g_modus; }
}; // namespace cma
......
......@@ -1316,57 +1316,42 @@ bool IsRunAsync(const PluginEntry &plugin) noexcept {
}
} // namespace provider::config
// #TODO simplify THIS TRASH, SK!
std::vector<char> RunSyncPlugins(PluginMap &plugins, int &total, int timeout) {
using DataBlock = std::vector<char>;
XLOG::d.t("To start [{}] sync plugins", plugins.size());
std::vector<std::future<DataBlock>> results;
int requested_count = 0;
total = 0;
if (timeout < 0) timeout = 1;
// sync part
using DataBlock = std::vector<char>;
void StartSyncPlugins(PluginMap &plugins,
std::vector<std::future<DataBlock>> &results,
int timeout) {
for (auto &entry_pair : plugins) {
auto &entry = entry_pair.second;
// check that out plugin is ging to run as async
auto run_async = cma::provider::config::IsRunAsync(entry);
if (run_async) continue;
if (provider::config::IsRunAsync(entry)) {
continue;
}
XLOG::t("Executing '{}'", entry.path());
// C++ async black magic
results.emplace_back(std::async(
std::launch::async, // first param
std::launch::async,
[](cma::PluginEntry *entry, int /*timeout*/) -> DataBlock {
if (entry == nullptr) {
return {};
}
return entry->getResultsSync(entry->path().wstring());
},
&entry, timeout));
}
}
[](cma::PluginEntry *plugin_entry,
int /*timeout*/) -> DataBlock { // lambda
if (plugin_entry == nullptr) return {};
return plugin_entry->getResultsSync(
plugin_entry->path().wstring());
}, // lambda end
DataBlock RunSyncPlugins(PluginMap &plugins, int &total, int timeout) {
XLOG::d.t("To start [{}] sync plugins", plugins.size());
&entry, // lambda parameter
timeout));
requested_count++;
}
std::vector<std::future<DataBlock>> results;
StartSyncPlugins(plugins, results, timeout);
// just check for ready futures
DataBlock out;
int delivered_count = 0;
for (auto &r : results) {
// auto status = r.wait_until(tm_to_stop);
// if (status == future_status::ready) {
auto result = r.get();
if (!result.empty()) {
++delivered_count;
tools::AddVector(out, result);
}
//} else {
// XLOG::t("skipped plugin");
//}
}
total = delivered_count;
......
......@@ -20,31 +20,32 @@ std::mutex g_run_command_processor_lock{};
} // namespace
bool RunCommand(std::string_view peer, std::string_view cmd) {
if (!cma::tools::IsEqual(peer, kMainPeer)) {
if (!tools::IsEqual(peer, kMainPeer)) {
XLOG::d("Peer name '{}' is invalid", peer);
return false;
}
if (cmd.empty()) return false;
if (cma::tools::IsEqual(cmd, kReload)) {
if (tools::IsEqual(cmd, kReload)) {
XLOG::l.t("Commander: Reload");
cma::ReloadConfig(); // command line
ReloadConfig(); // command line
return true;
}
if (cma::tools::IsEqual(cmd, kPassTrue)) {
if (tools::IsEqual(cmd, kPassTrue)) {
XLOG::l.t("Commander: Pass True");
return true;
}
if (cma::tools::IsEqual(cmd, kUninstallAlert)) {
if (tools::IsEqual(cmd, kUninstallAlert)) {
XLOG::l.t("Commander: Alert of Uninstall");
if (cma::IsTest()) return false;
if (!cma::IsService()) return false;
if (GetModus() != Modus::service) {
return false;
}
cma::g_uninstall_alert.set();
g_uninstall_alert.set();
return true;
}
......
......@@ -4,6 +4,7 @@
#include <chrono>
#include <iostream>
#include <ranges>
#include "agent_controller.h"
#include "asio.h"
......@@ -13,6 +14,7 @@
using asio::ip::tcp;
using namespace std::chrono_literals;
namespace rs = std::ranges;
// This namespace contains classes used for external communication, for example
// with Monitor
......@@ -249,6 +251,16 @@ bool IsIpAllowedAsException(const std::string &ip) {
(ip == "127.0.0.1" || ip == "::1");
}
namespace {
std::vector<Modus> local_connection_moduses{Modus::service, Modus::test,
Modus::integration};
bool AllowLocalConnection() {
return rs::find(local_connection_moduses, GetModus()) !=
local_connection_moduses.end();
}
} // namespace
// singleton thread
void ExternalPort::processQueue(const world::ReplyFunc &reply) {
while (true) {
......@@ -269,7 +281,7 @@ void ExternalPort::processQueue(const world::ReplyFunc &reply) {
bool local_connection = ip == "127.0.0.1" || ip == "::1";
if (cfg::groups::global.isIpAddressAllowed(ip) ||
local_connection) {
if (local_connection && (IsService() || IsTest())) {
if (local_connection && AllowLocalConnection()) {
as->read_ip();
}
as->start(reply);
......@@ -280,7 +292,9 @@ void ExternalPort::processQueue(const world::ReplyFunc &reply) {
XLOG::l.crit("Memory usage is too high [{}]",
wtools::GetOwnVirtualSize());
#if defined(TEST_RESTART_OVERLOAD)
if (IsService()) std::terminate();
if (GetModus() == Modus::service) {
std::terminate();
}
#else
#pragma message("**************************************")
#pragma message("ATTENTION: Your has no RESTART on overload")
......
......@@ -14,6 +14,8 @@
#include "tools/_raii.h" // on out
#include "tools/_tgt.h" // we need IsDebug
namespace fs = std::filesystem;
namespace cma::cfg {
Global::Global() {
......@@ -174,14 +176,16 @@ void Global::updateLogNames() {
// empty string does nothing
// used to set values during start
void Global::setLogFolder(const std::filesystem::path &forced_path) {
void Global::setLogFolder(const fs::path &forced_path) {
std::unique_lock lk(lock_);
if (cma::IsService()) {
if (GetModus() == Modus::service) {
XLOG::details::LogWindowsEventAlways(
XLOG::EventLevel::information, 35,
"checkmk service uses log path '{}'", forced_path);
}
if (forced_path.empty()) return;
if (forced_path.empty()) {
return;
}
yaml_log_path_ = CheckAndCreateLogPath(forced_path);
......
......@@ -281,8 +281,9 @@ void Emitter::postProcessAndPrint(const std::string &text) {
// EVENT
if (setup::IsEventLogEnabled() && ((dirs & xlog::kEventPrint) != 0)) {
// we do not need to format string for the event
auto windows_event_log_id = cma::IsService() ? EventClass::kSrvDefault
: EventClass::kAppDefault;
auto windows_event_log_id = cma::GetModus() == cma::Modus::service
? EventClass::kSrvDefault
: EventClass::kAppDefault;
details::LogWindowsEventCritical(windows_event_log_id, text.c_str());
}
......
......@@ -756,7 +756,7 @@ std::wstring ModuleCommander::buildCommandLine(std::string_view filename) {
fs::path ModuleCommander::GetMoveLocation(const fs::path &module_file) {
return fs::temp_directory_path() /
(std::string{g_module_uninstall_path} +
(cma::IsService() ? "_srv" : "_app")) /
(GetModus() == Modus::service ? "_srv" : "_app")) /
module_file.filename();
}
......
......@@ -14,10 +14,6 @@
namespace fs = std::filesystem;
namespace cma::details {
extern bool g_is_test;
}
namespace cma {
// internal global variables:
......@@ -104,8 +100,13 @@ bool FindAndPrepareWorkingFolders(AppType app_type) {
} // namespace cfg
static AppType CalcAppType(AppType app_type) {
if (app_type == AppType::automatic) return AppDefaultType();
if (app_type == AppType::test) cma::details::g_is_test = true;
if (app_type == AppType::automatic) {
return AppDefaultType();
}
if (app_type == AppType::test) {
details::SetModus(Modus::test);
}
return app_type;
}
......@@ -125,7 +126,7 @@ void UninstallAlert::clear() noexcept {
void UninstallAlert::set() noexcept {
//
if (!IsService()) {
if (GetModus() != Modus::service) {
XLOG::l.i("Requested clean on exit is IGNORED, not service");
return;
}
......@@ -180,7 +181,7 @@ bool OnStart(AppType proposed_type, const std::wstring &config_file) {
}
if (!already_loaded) {
XLOG::setup::SetContext(cma::IsService() ? "srv" : "app");
XLOG::setup::SetContext(GetModus() == Modus::service ? "srv" : "app");
return OnStartCore(type, config_file);
}
......
......@@ -125,7 +125,7 @@ void ServiceProcessor::stopService() {
void ServiceProcessor::cleanupOnStop() {
XLOG::l.i("Cleanup called by service");
if (!IsService()) {
if (GetModus() != Modus::service) {
XLOG::l("Invalid call!");
}
KillAllInternalUsers();
......@@ -636,9 +636,17 @@ std::optional<ServiceProcessor::ControllerParam> OptionallyStartAgentController(
return {};
}
std::wstring_view RuleName() {
switch (GetModus()) {
case Modus::service:
return srv::kSrvFirewallRuleName;
default:
return srv::kAppFirewallRuleName;
}
}
void OpenFirewall(bool controller) {
auto rule_name =
IsService() ? srv::kSrvFirewallRuleName : srv::kAppFirewallRuleName;
auto rule_name = RuleName();
if (controller) {
XLOG::l.i("Controller has started: firewall to controller");
ProcessFirewallConfiguration(ac::GetWorkController().wstring(),
......@@ -659,7 +667,8 @@ void OpenFirewall(bool controller) {
/// Periodically checks if the service is stopping.
void ServiceProcessor::mainThread(world::ExternalPort *ex_port,
bool cap_installed) noexcept {
if (IsService()) {
auto is_service = GetModus() == Modus::service;
if (is_service) {
auto wait_period =
cfg::GetVal(cfg::groups::kSystem, cfg::vars::kWaitNetwork,
cfg::defaults::kServiceWaitNetwork);
......@@ -675,17 +684,17 @@ void ServiceProcessor::mainThread(world::ExternalPort *ex_port,
ac::kCmkAgentUnistall,
controller_params.has_value());
}
MailSlot mailbox(
IsService() ? cfg::kServiceMailSlot : cfg::kTestingMailSlot, 0);
MailSlot mailbox(is_service ? cfg::kServiceMailSlot : cfg::kTestingMailSlot,
0);
internal_port_ = carrier::BuildPortName(carrier::kCarrierMailslotName,
mailbox.GetName());
try {
mailbox.ConstructThread(SystemMailboxCallback, 20, this,
IsService() ? wtools::SecurityLevel::admin
: wtools::SecurityLevel::standard);
is_service ? wtools::SecurityLevel::admin
: wtools::SecurityLevel::standard);
ON_OUT_OF_SCOPE(mailbox.DismantleThread());
if (IsService()) {
if (is_service) {
mc_.InstallDefault(cfg::modules::InstallMode::normal);
install::ClearPostInstallFlag();
} else {
......
......@@ -92,7 +92,7 @@ constexpr const wchar_t *kServiceAccount = L"NT AUTHORITY\\LocalService";
constexpr const wchar_t *kServicePassword = nullptr;
constexpr std::wstring_view kSrvFirewallRuleName = L"Checkmk Agent";
constexpr std::wstring_view kIntFirewallRuleName = L"Checkmk Agent Internal";
constexpr std::wstring_view kIntFirewallRuleName = L"Checkmk Agent Integration";
constexpr std::wstring_view kAppFirewallRuleName = L"Checkmk Agent application";
constexpr std::wstring_view kTstFirewallRuleName = L"Checkmk Agent TEST";
......
......@@ -12,6 +12,7 @@
#include "cfg.h"
#include "cma_core.h"
#include "common/cfg_info.h"
#include "common/cmdline_info.h"
#include "common/yaml.h"
#include "install_api.h"
......@@ -263,13 +264,9 @@ void ServiceUsage(std::wstring_view comment) {
} // namespace cma::cmdline
namespace cma::details {
extern bool g_is_service;
} // namespace cma::details
namespace cma {
AppType AppDefaultType() {
return details::g_is_service ? AppType::srv : AppType::exe;
return GetModus() == Modus::service ? AppType::srv : AppType::exe;
}
namespace {
......@@ -358,7 +355,7 @@ int CheckMainService(const std::wstring &param, int interval) {
namespace srv {
int RunService(std::wstring_view app_name) {
cma::details::g_is_service = true; // we know that we are service
cma::details::SetModus(Modus::service); // we know that we are service
auto ret = ServiceAsService(app_name, 1000ms, []() -> bool {
// Auto Update when MSI file is located by specified address
......
......@@ -14,9 +14,6 @@
using namespace std::chrono_literals;
namespace fs = std::filesystem;
namespace rs = std::ranges;
namespace cma::details {
extern bool g_is_service;
}
namespace cma::ac {
TEST(AgentController, StartAgent) {
......@@ -395,8 +392,9 @@ TEST_F(AgentControllerCreateArtifacts, From1620OldWithController) {
}
TEST(AgentController, SimulationIntegration) {
details::g_is_service = true;
ON_OUT_OF_SCOPE(details::g_is_service = false;);
const auto m = GetModus();
ON_OUT_OF_SCOPE(details::SetModus(m));
details::SetModus(Modus::service);
auto temp_fs = tst::TempCfgFs::Create();
ASSERT_TRUE(temp_fs->loadFactoryConfig());
fs::copy(fs::path{"c:\\windows\\system32\\whoami.exe"},
......
......@@ -29,11 +29,6 @@ namespace fs = std::filesystem;
// we want to avoid those data public
namespace cma {
void ResetCleanOnExit();
namespace details {
extern bool g_is_service;
extern bool g_is_test;
} // namespace details
} // namespace cma
namespace cma::commander {
......@@ -415,10 +410,12 @@ TEST(Cma, CleanApi) {
alert.set();
ASSERT_FALSE(alert.isSet())
<< "forbidden to set for non service executable";
cma::details::g_is_service = true;
const auto m = GetModus();
ON_OUT_OF_SCOPE(cma::details::SetModus(m));
cma::details::SetModus(cma::Modus::service);
alert.set();
EXPECT_TRUE(alert.isSet());
cma::details::g_is_service = false;
cma::details::SetModus(m);
alert.clear();
EXPECT_FALSE(alert.isSet());