diff --git a/lib/ApplicationFeatures/ApplicationServer.cpp b/lib/ApplicationFeatures/ApplicationServer.cpp index 3675cdaf55..caf005f932 100644 --- a/lib/ApplicationFeatures/ApplicationServer.cpp +++ b/lib/ApplicationFeatures/ApplicationServer.cpp @@ -228,6 +228,10 @@ void ApplicationServer::beginShutdown() { // to run method } +void ApplicationServer::shutdownFatalError() { + reportServerProgress(ServerState::ABORT); +} + VPackBuilder ApplicationServer::options( std::unordered_set const& excludes) const { return _options->toVPack(false, excludes); diff --git a/lib/ApplicationFeatures/ApplicationServer.h b/lib/ApplicationFeatures/ApplicationServer.h index 1eafcdb811..aa51f37b3b 100644 --- a/lib/ApplicationFeatures/ApplicationServer.h +++ b/lib/ApplicationFeatures/ApplicationServer.h @@ -44,7 +44,8 @@ enum class ServerState { IN_START, IN_WAIT, IN_STOP, - STOPPED + STOPPED, + ABORT }; class ProgressHandler { @@ -191,6 +192,9 @@ class ApplicationServer { // signal the server to shut down void beginShutdown(); + // report that we are going down by fatal error + void shutdownFatalError(); + // return VPack options VPackBuilder options(std::unordered_set const& excludes) const; diff --git a/lib/ApplicationFeatures/WindowsServiceFeature.cpp b/lib/ApplicationFeatures/WindowsServiceFeature.cpp index f0855d4070..8f0962621e 100644 --- a/lib/ApplicationFeatures/WindowsServiceFeature.cpp +++ b/lib/ApplicationFeatures/WindowsServiceFeature.cpp @@ -67,6 +67,12 @@ static std::string FriendlyServiceName = "ArangoDB - the multi-model database"; static SERVICE_STATUS_HANDLE ServiceStatus; +void reportServiceAborted(void) { + if (ArangoInstance != nullptr && ArangoInstance->_server != nullptr) { + ArangoInstance->_server->beginShutdown(); + } +} + // So we have a valid minidump area during startup: void WindowsServiceFeature::StartArangoService (bool WaitForRunning) { TRI_ERRORBUF; @@ -290,6 +296,71 @@ void WindowsServiceFeature::installService() { CloseServiceHandle(schService); } +void WindowsServiceFeature::DeleteService (bool force) { + CHAR path[MAX_PATH] = ""; + + if (! GetModuleFileNameA(nullptr, path, MAX_PATH)) { + std::cerr << "FATAL: GetModuleFileNameA failed" << std::endl; + exit(EXIT_FAILURE); + } + + std::cout << "INFO: removing service '" << ServiceName << "'" << std::endl; + + SC_HANDLE schSCManager = OpenSCManager(nullptr, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); + + if (schSCManager == 0) { + std::cerr << "FATAL: OpenSCManager failed with " << GetLastError() << std::endl; + exit(EXIT_FAILURE); + } + + SC_HANDLE schService = OpenServiceA( + schSCManager, // SCManager database + ServiceName.c_str(), // name of service + DELETE|SERVICE_QUERY_CONFIG); // first validate whether its us, then delete. + + char serviceConfigMemory[8192]; // msdn says: 8k is enough. + DWORD bytesNeeded = 0; + if (QueryServiceConfig(schService, + (LPQUERY_SERVICE_CONFIGA)&serviceConfigMemory, + sizeof(serviceConfigMemory), + &bytesNeeded)) { + QUERY_SERVICE_CONFIG *cfg = (QUERY_SERVICE_CONFIG*) &serviceConfigMemory; + + std::string command = std::string("\"") + std::string(path) + std::string("\" --start-service"); + if (strcmp(cfg->lpBinaryPathName, command.c_str())) { + if (! force) { + std::cerr << "NOT removing service of other installation: " << + cfg->lpBinaryPathName << + " Our path is: " << + path << std::endl; + + CloseServiceHandle(schSCManager); + return; + } + else { + std::cerr << "Removing service of other installation because of FORCE: " << + cfg->lpBinaryPathName << + "Our path is: " << + path << std::endl; + } + } + } + + CloseServiceHandle(schSCManager); + + if (schService == 0) { + std::cerr << "FATAL: OpenServiceA failed with " << GetLastError() << std::endl; + exit(EXIT_FAILURE); + } + + if (! DeleteService(schService)) { + std::cerr << "FATAL: DeleteService failed with " << GetLastError() << std::endl; + exit(EXIT_FAILURE); + } + + CloseServiceHandle(schService); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief flips the status for a service @@ -357,6 +428,24 @@ void WindowsServiceFeature::shutDownBegins () { } +////////////////////////////////////////////////////////////////////////////// +/// @brief wrap ArangoDB server so we can properly emmit a status on shutdown +/// starting +////////////////////////////////////////////////////////////////////////////// +void WindowsServiceFeature::shutDownComplete () { + // startup finished - signalize we're running. + SetServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0); +} + +////////////////////////////////////////////////////////////////////////////// +/// @brief wrap ArangoDB server so we can properly emmit a status on shutdown +/// starting +////////////////////////////////////////////////////////////////////////////// +void WindowsServiceFeature::shutDownFailure () { + // startup finished - signalize we're running. + SetServiceStatus(SERVICE_STOP, ERROR_FAIL_RESTART, 0, 0); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief service control handler //////////////////////////////////////////////////////////////////////////////// @@ -467,15 +556,15 @@ void WindowsServiceFeature::collectOptions(std::shared_ptr optio } void WindowsServiceFeature::validateOptions(std::shared_ptr options) { - if (_startAsService) { - } - else if (_installService) { + + if (_installService) { installService(); exit(EXIT_SUCCESS); } else if (_unInstallService) { } - + else if (_forceUninstall) { + } else if (_startAsService) { ProgressHandler reporter{ [this](ServerState state) { diff --git a/lib/ApplicationFeatures/WindowsServiceFeature.h b/lib/ApplicationFeatures/WindowsServiceFeature.h index 52c0666f5e..32d201dbca 100644 --- a/lib/ApplicationFeatures/WindowsServiceFeature.h +++ b/lib/ApplicationFeatures/WindowsServiceFeature.h @@ -36,6 +36,7 @@ class WindowsServiceFeature final : public application_features::ApplicationFeat private: void installService(); + void DeleteService (bool force); void StartArangoService (bool WaitForRunning); void StopArangoService (bool WaitForShutdown); void startupProgress (); @@ -43,6 +44,8 @@ class WindowsServiceFeature final : public application_features::ApplicationFeat void startupFinished (); void shutDownBegins (); + void shutDownComplete (); + void shutDownFailure (); public: bool _installService = false; diff --git a/lib/Basics/win-utils.cpp b/lib/Basics/win-utils.cpp index cd807ea640..b2848c656f 100644 --- a/lib/Basics/win-utils.cpp +++ b/lib/Basics/win-utils.cpp @@ -619,7 +619,16 @@ void ADB_WindowsEntryFunction() { TRI_Application_Exit_SetExit(ADB_WindowsExitFunction); } +TRI_serviceAboert_t serviceAbort = nullptr; + +void TRI_SetWindowsServiceAbortFunction(TRI_serviceAboert_t f) { + serviceAbort = f; +} + void ADB_WindowsExitFunction(int exitCode, void* data) { + if (serviceAbort != nullptr) { + serviceAbort(); + } int res = finalizeWindows(TRI_WIN_FINAL_WSASTARTUP_FUNCTION_CALL, 0); if (res != 0) { diff --git a/lib/Basics/win-utils.h b/lib/Basics/win-utils.h index e313ec5829..13a9244c6e 100644 --- a/lib/Basics/win-utils.h +++ b/lib/Basics/win-utils.h @@ -108,6 +108,10 @@ int TRI_MapSystemError(DWORD); bool TRI_InitWindowsEventLog(void); void TRI_CloseWindowsEventlog(void); +typedef void (*TRI_serviceAboert_t)(void); + +void TRI_SetWindowsServiceAbortFunction(TRI_serviceAboert_t); + //////////////////////////////////////////////////////////////////////////////// /// @brief logs a message to the windows event log. /// we rather are keen on logging something at all then on being able to work