Skip to content

Commit

Permalink
[CMK-2245] - command line cleanup and improvement
Browse files Browse the repository at this point in the history
- check_mkagent.exe without parameters work now as help
- install & remove options are moved to the advanced option and processed correctly
- unit tests install/remove revived
- many typos fixed
- removed excessive angled brackets

Change-Id: I04a08997cab29c2977ccdc5818801dd3497d11fa
  • Loading branch information
s-kipnis committed Jun 19, 2019
1 parent 055bbd1 commit 03345f4
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 58 deletions.
84 changes: 55 additions & 29 deletions agents/wnx/src/common/wtools.cpp
Expand Up @@ -4,6 +4,7 @@
#define WIN32_LEAN_AND_MEAN
#include <WinSock2.h>

#include <WinError.h>
#include <stdio.h>
#include <windows.h>

Expand Down Expand Up @@ -96,17 +97,18 @@ void WINAPI ServiceController::ServiceMain(DWORD Argc, wchar_t** Argv) {

// no return from here
// can print on screen
bool ServiceController::registerAndRun(const wchar_t* ServiceName, //
bool CanStop, // t
bool CanShutdown, // t
bool CanPauseContinue) { // t
ServiceController::StopType ServiceController::registerAndRun(
const wchar_t* ServiceName, //
bool CanStop, // t
bool CanShutdown, // t
bool CanPauseContinue) { // t
if (!processor_) {
XLOG::l.bp("No processor");
return false;
return StopType::fail;
}
if (!ServiceName) {
XLOG::l.bp("No Service name");
return false;
return StopType::fail;
}

// strange code below
Expand All @@ -128,23 +130,30 @@ bool ServiceController::registerAndRun(const wchar_t* ServiceName, //
// returns when the service has stopped. The process should simply
// terminate when the call returns. Two words: Blocks Here
try {
auto ret = StartServiceCtrlDispatcher(serviceTable);
auto ret = ::StartServiceCtrlDispatcher(serviceTable);
if (!ret) {
auto error = GetLastError();

// this normal situation when we are starting service from command
// line without parameters
if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
return StopType::no_connect;

XLOG::l(XLOG::kStdio)
.crit("Cannot Start Service '{}' error = [{}]",
ConvertToUTF8(ServiceName), GetLastError());
return false;
ConvertToUTF8(ServiceName), error);
return StopType::fail;
}
return true;
return StopType::normal;
} catch (std::exception& e) {
XLOG::l(XLOG::kStdio)
.crit("Exception '{}' in Service start with error {}", e.what(),
.crit("Exception '{}' in Service start with error [{}]", e.what(),
GetLastError());
} catch (...) {
XLOG::l(XLOG::kStdio)
.crit("Exception in Service start with error {}", GetLastError());
.crit("Exception in Service start with error [{}]", GetLastError());
}
return false;
return StopType::fail;
}

//
Expand Down Expand Up @@ -209,13 +218,20 @@ bool InstallService(const wchar_t* ServiceName, const wchar_t* DisplayName,
Password // Password of the account
);
if (!service) {
XLOG::l(XLOG::kStdio)
.crit("CreateService failed w/err {}", GetLastError());
return false;
auto error = GetLastError();
if (error == ERROR_SERVICE_EXISTS) {
XLOG::l(XLOG::kStdio)
.crit("The Service '{}' already exists",
wtools::ConvertToUTF8(ServiceName));
return false;
}
XLOG::l(XLOG::kStdio).crit("CreateService failed w/err {}", error);
}
ON_OUT_OF_SCOPE(CloseServiceHandle(service););

XLOG::l(XLOG::kStdio).i("'{}' is installed.", ConvertToUTF8(ServiceName));
XLOG::l(XLOG::kStdio)
.i("The Service '{}' is installed.", ConvertToUTF8(ServiceName));

return true;
}

Expand All @@ -232,33 +248,43 @@ bool InstallService(const wchar_t* ServiceName, const wchar_t* DisplayName,
// error in the standard output stream for users to diagnose the problem.
//
// StopService by default is true, use false only during testing
bool UninstallService(const wchar_t* ServiceName, UninstallServiceMode Mode) {
bool UninstallService(const wchar_t* service_name,
UninstallServiceMode uninstall_mode) {
XLOG::setup::ColoredOutputOnStdio(true);
if (service_name == nullptr) {
XLOG::l(XLOG::kStdio).crit("Parameter is null");
return false;
}
auto name = wtools::ConvertToUTF8(service_name);
// Open the local default service control manager database
auto service_manager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (!service_manager) {
XLOG::l(XLOG::kStdio)
.crit("OpenSCManager failed w/err {:#X}", GetLastError());
.crit("OpenSCManager failed, [{}]", GetLastError());
return false;
}
ON_OUT_OF_SCOPE(::CloseServiceHandle(service_manager););

// Open the service with delete, stop, and query status permissions
auto service = ::OpenService(service_manager, ServiceName,
auto service = ::OpenService(service_manager, service_name,
SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE);
if (!service) {
XLOG::l(XLOG::kStdio)
.crit("OpenService failed w/err {:#X}", GetLastError());
auto error = GetLastError();
if (error == ERROR_SERVICE_DOES_NOT_EXIST) {
XLOG::l(XLOG::kStdio).crit("The Service '{}' doesn't exist", name);
return false;
}

XLOG::l(XLOG::kStdio).crit("OpenService '{}' failed, [{}]", name);
return false;
}
ON_OUT_OF_SCOPE(::CloseServiceHandle(service););

if (Mode == UninstallServiceMode::normal) {
if (uninstall_mode == UninstallServiceMode::normal) {
// Try to stop the service
SERVICE_STATUS ssSvcStatus = {};
auto service_name = wtools::ConvertToUTF8(ServiceName);
if (::ControlService(service, SERVICE_CONTROL_STOP, &ssSvcStatus)) {
XLOG::l(XLOG::kStdio).i("Stopping '{}'.", service_name);
XLOG::l(XLOG::kStdio).i("Stopping '{}'.", name);
Sleep(1000);

while (::QueryServiceStatus(service, &ssSvcStatus)) {
Expand All @@ -270,22 +296,22 @@ bool UninstallService(const wchar_t* ServiceName, UninstallServiceMode Mode) {
}

if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) {
XLOG::l(XLOG::kStdio).i("\n{} is stopped.", service_name);
XLOG::l(XLOG::kStdio).i("\n{} is stopped.", name);
} else {
XLOG::l(XLOG::kStdio).i("\n{} failed to stop.", service_name);
XLOG::l(XLOG::kStdio).i("\n{} failed to stop.", name);
}
}
}

// Now remove the service by calling DeleteService.
if (!::DeleteService(service)) {
XLOG::l(XLOG::kStdio)
.i("DeleteService failed w/err {:#X}\n", GetLastError());
.i("DeleteService for '{}' failed [{}]\n", name, GetLastError());
return false;
}

XLOG::l(XLOG::kStdio)
.i("%s is removed.\n", wtools::ConvertToUTF8(ServiceName));
.i("The Service '{}' is successfully removed.\n", name);
return true;
}

Expand Down
12 changes: 7 additions & 5 deletions agents/wnx/src/common/wtools.h
Expand Up @@ -73,8 +73,9 @@ bool InstallService(const wchar_t* ServiceName, const wchar_t* DisplayName,
// error in the standard output stream for users to diagnose the problem.
//
enum class UninstallServiceMode { normal, test };
bool UninstallService(const wchar_t* ServiceName,
UninstallServiceMode Mode = UninstallServiceMode::normal);
bool UninstallService(
const wchar_t* service_name,
UninstallServiceMode uninstall_mode = UninstallServiceMode::normal);

// Abstract Interface template for SERVICE PROCESSOR:
// WE ARE NOT GOING TO USE AT ALL.
Expand Down Expand Up @@ -363,9 +364,10 @@ class ServiceController {
}

// no return from here till service ends
bool registerAndRun(const wchar_t* ServiceName, //
bool CanStop = true, bool CanShutdown = true,
bool CanPauseContinue = true);
enum class StopType { normal, no_connect, fail };
StopType registerAndRun(const wchar_t* ServiceName, //
bool CanStop = true, bool CanShutdown = true,
bool CanPauseContinue = true);

protected:
//
Expand Down
24 changes: 19 additions & 5 deletions agents/wnx/src/engine/windows_service_api.cpp
Expand Up @@ -191,6 +191,10 @@ int TestMainServiceSelf(int Interval) {
asio::ip::tcp::socket socket(ios);
std::error_code ec;

// give some time to start main thread
// this is tesing routine ergo so primitive method is ok
cma::tools::sleep(1000);

while (!stop) {
auto enc = cma::cfg::groups::global.globalEncrypt();
auto password = enc ? cma::cfg::groups::global.password() : "";
Expand Down Expand Up @@ -239,6 +243,7 @@ int TestMainServiceSelf(int Interval) {
if (Interval == 0) break;
}
XLOG::l.i("Leaving testing thread");
if (Interval == 0) XLOG::l.i("\n\nPress any key to end program\n\n");
});

ExecMainService(StdioLog::no); // blocking call waiting for keypress
Expand All @@ -252,10 +257,10 @@ int TestMainServiceSelf(int Interval) {
return 0;
}

// on -test
// on check
int TestMainService(const std::wstring& What, int Interval) {
using namespace std::chrono;
if (What == L"port") {
if (What == L"io") {
// simple test for ExternalPort. will be disabled in production.
try {
cma::world::ExternalPort port(nullptr);
Expand Down Expand Up @@ -690,9 +695,18 @@ int ServiceAsService(
cma::srv::kServiceName); // we will stay here till
// service will be stopped
// itself or from outside
XLOG::l.i("Service is stopped {}",
ret ? "as usually" : "due to abnormal situation");
return ret ? 0 : -1;
switch (ret) {
case wtools::ServiceController::StopType::normal:
XLOG::l.i("Service is stopped normally");
return 0;

case wtools::ServiceController::StopType::fail:
XLOG::l.i("Service is stopped due to abnormal situation");
return -1;
case wtools::ServiceController::StopType::no_connect:
// may happen when we try to call usual exe
return 0;
}
} catch (const std::exception& e) {
XLOG::l.crit("Exception hit {} in ServiceAsService", e.what());
} catch (...) {
Expand Down
39 changes: 29 additions & 10 deletions agents/wnx/src/main/check_mk_service.cpp
Expand Up @@ -38,14 +38,13 @@ void PrintMain() {
using namespace xlog::internal;
PrintBlock("Normal Usage:\n", Colors::kGreen, []() {
return fmt::format(
"\t{1} <{2}|{3}|{4}>\n"
"\t{2:<{0}} - install as a service, Administrative Rights are required\n"
"\t{3:<{0}} - remove service, Administrative Rights are required\n"
"\t{4:<{0}} - usage\n",
"\t{1} <{2}|{3}>\n"
"\t{2:<{0}} - usage\n"
"\t{3:<{0}} - test\n",
kParamShift,
kServiceExeName, // service name from th project definitions
// first Row
kInstallParam, kRemoveParam, kHelpParam);
kLegacyTestParam, kHelpParam);
});
}

Expand All @@ -56,10 +55,10 @@ void PrintSelfCheck() {
"\t{1} {2} <{3}|{4}|{5} [number of seconds]>\n"
"\t{2:<{0}} - check test\n"
"\t\t{3:<{0}} - main thread test\n"
"\t\t{4:<{0}} - internal port test \n"
"\t\t{5:<{0}} - simulates connection after expiring 'seconds' interval\n",
"\t\t{4:<{0}} - simple self test of internal and external transport\n"
"\t\t{5:<{0}} - simulates periodical connection from Check MK Site, for example '{1} {2} {5} 13'\n",
kParamShift, kServiceExeName, kCheckParam, kCheckParamMt,
kCheckParamPort, kCheckParamSelf);
kCheckParamIo, kCheckParamSelf);
});
}

Expand All @@ -78,6 +77,7 @@ void PrintAdHoc() {
});
}

// obsolete
void PrintLegacyTesting() {
using namespace xlog::internal;
PrintBlock("Classic/Legacy Testing:\n", Colors::kCyan, []() {
Expand All @@ -90,6 +90,22 @@ void PrintLegacyTesting() {
});
}

void PrintInstallUninstall() {
using namespace xlog::internal;
PrintBlock(
"To install or remove service: for Experienced Users only:\n",
Colors::kPink, []() {
return fmt::format(
"\t{1} <{2}|{3}>\n"
"\t{2:<{0}} - install as a service, Administrative Rights are required\n"
"\t{3:<{0}} - remove service, Administrative Rights are required\n",
kParamShift,
kServiceExeName, // service name from th project definitions
// first Row
kInstallParam, kRemoveParam);
});
}

void PrintShowConfig() {
using namespace xlog::internal;
PrintBlock(
Expand Down Expand Up @@ -206,14 +222,14 @@ static void ServiceUsage(std::wstring_view comment) {
PrintMain();
PrintSelfCheck();
PrintAdHoc();
PrintLegacyTesting();
PrintRealtimeTesting();
PrintShowConfig();
PrintCvt();
PrintLwaActivate();
PrintUpgrade();
PrintCap();
PrintSectionTesting();
PrintInstallUninstall();
} catch (const std::exception &e) {
XLOG::l("Exception is '{}'", e.what()); //
}
Expand Down Expand Up @@ -284,7 +300,7 @@ int MainFunction(int argc, wchar_t const *Argv[]) {

cma::details::G_Service = true; // we know that we are service

return cma::srv::ServiceAsService(1000ms, [](const void *) {
auto ret = cma::srv::ServiceAsService(1000ms, [](const void *) {
// optional commands listed here
// ********
// 1. Auto Update when MSI file is located by specified address
Expand All @@ -298,6 +314,9 @@ int MainFunction(int argc, wchar_t const *Argv[]) {
GetUserInstallDir()); // dir where file to backup
return true;
});
if (ret == 0) ServiceUsage(L"");

return ret == 0 ? 0 : 1;
}

std::wstring param(Argv[1]);
Expand Down
2 changes: 1 addition & 1 deletion agents/wnx/src/main/check_mk_service.h
Expand Up @@ -19,7 +19,7 @@ constexpr std::string_view kLegacyTestParam = "test";
constexpr std::string_view kCheckParam = "check";
constexpr std::string_view kCheckParamSelf = "-self";
constexpr std::string_view kCheckParamMt = "-mt";
constexpr std::string_view kCheckParamPort = "-port";
constexpr std::string_view kCheckParamIo = "-io";

constexpr std::string_view kRealtimeParam = "rt";
constexpr std::string_view kHelpParam = "help";
Expand Down
17 changes: 9 additions & 8 deletions agents/wnx/watest/test-service.cpp
Expand Up @@ -91,7 +91,9 @@ TEST(ServiceControllerTest, StartStop) {
EXPECT_EQ(controller.can_shutdown_, false);
EXPECT_EQ(controller.can_pause_continue_, false);
EXPECT_NE(controller.processor_, nullptr);
if (0) {

// special case with "no connect" case
{
auto ret =
wtools::InstallService(test_service_name, // name of service
L"Test Name", // service name to display
Expand All @@ -101,19 +103,18 @@ TEST(ServiceControllerTest, StartStop) {
nullptr // no password
);
EXPECT_TRUE(ret);

if (ret) {
bool success = false;
ON_OUT_OF_SCOPE(wtools::UninstallService(test_service_name));
auto success = wtools::ServiceController::StopType::fail;
std::thread t([&]() {
success = controller.registerAndRun(test_service_name, true,
true, true);
});
EXPECT_TRUE(success);
std::this_thread::sleep_until(steady_clock::now() +
500ms); // wait for thread
EXPECT_TRUE(counter > 3);
if (t.joinable()) t.join();

EXPECT_TRUE(ret);
if (ret) wtools::UninstallService(test_service_name);
EXPECT_EQ(success, wtools::ServiceController::StopType::no_connect);
EXPECT_EQ(counter, 0);
}
}
}
Expand Down

0 comments on commit 03345f4

Please sign in to comment.