提交 959c9d34 编写于 作者: J jonathan pickett

Added preshutdown notification support to service to deal with Issue 87. Also...

Added preshutdown notification support to service to deal with Issue 87. Also improved message filter support for eventlog.
上级 2d29a8f2
......@@ -99,7 +99,7 @@ void redisLogRaw(int level, const char *msg) {
if (logFile) fclose(fp);
#ifdef _WIN32
LogToEventLog(server.syslog_ident, msg);
if (server.syslog_enabled) WriteEventLog(server.syslog_ident, msg);
#else
if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);
#endif
......
#include <Windows.h>
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
#include "Win32_EventLog.h"
#include "Win32_SmartHandle.h"
#include "EventLog.h"
class RedisEventLog {
public:
static RedisEventLog& getInstance() {
static RedisEventLog instance; // Instantiated on first use. Guaranteed to be destroyed.
return instance;
void RedisEventLog::UninstallEventLogSource() {
SmartRegistryHandle appKey;
if (ERROR_SUCCESS == RegOpenKeyA(HKEY_LOCAL_MACHINE, cEventLogApplicitonPath.c_str(), appKey)) {
SmartRegistryHandle eventLogNameKey;
if (ERROR_SUCCESS == RegOpenKeyA(appKey, eventLogName.c_str(), eventLogNameKey)) {
if (ERROR_SUCCESS != RegDeleteKeyA(appKey, eventLogName.c_str())) {
throw std::system_error(GetLastError(), system_category(), "RegDeleteKeyA failed");
}
}
}
private:
std::string eventLogName;
SmartRegistryHandle eventLogKey;
if (ERROR_SUCCESS == RegOpenKeyA(HKEY_LOCAL_MACHINE, cEventLogPath.c_str(), eventLogKey)) {
SmartRegistryHandle eventServiceKey;
if (ERROR_SUCCESS == RegOpenKeyA(eventLogKey, cRedis.c_str(), eventServiceKey)) {
SmartRegistryHandle eventServiceSubKey;
if (ERROR_SUCCESS == RegOpenKeyA(eventServiceKey, cRedisServer.c_str(), eventServiceSubKey)) {
if (ERROR_SUCCESS != RegDeleteKeyA(eventServiceKey, cRedisServer.c_str())) {
throw std::system_error(GetLastError(), system_category(), "RegDeleteKeyA failed");
}
if (ERROR_SUCCESS != RegDeleteKeyA(eventLogKey, cRedis.c_str())) {
throw std::system_error(GetLastError(), system_category(), "RegDeleteKeyA failed");
}
}
}
}
}
RedisEventLog() {
// sets up the registry keys required for the EventViewer message filter
void RedisEventLog::InstallEventLogSource(string appPath) {
SmartRegistryHandle eventLogKey;
if (ERROR_SUCCESS != RegOpenKeyA(HKEY_LOCAL_MACHINE, cEventLogPath.c_str(), eventLogKey)) {
throw std::system_error(GetLastError(), system_category(), "RegOpenKey failed");
}
~RedisEventLog() {
UninstallEventLogSource();
SmartRegistryHandle redis1;
if (ERROR_SUCCESS != RegOpenKeyA(eventLogKey, cRedis.c_str(), redis1)) {
if (ERROR_SUCCESS != RegCreateKeyA(eventLogKey, cRedis.c_str(), redis1)) {
throw std::system_error(GetLastError(), system_category(), "RegCreateKeyA failed");
}
}
RedisEventLog(RedisEventLog const&); // Don't implement to guarantee singleton semantics
void operator=(RedisEventLog const&); // Don't implement to guarantee singleton semantics
void UninstallEventLogSource() {
const std::string keyPath(
"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\" + this->eventLogName);
DWORD last_error = RegDeleteKeyA(HKEY_LOCAL_MACHINE, keyPath.c_str());
if (ERROR_SUCCESS != last_error) {
std::cerr << "Failed to uninstall source: " << last_error << endl;
SmartRegistryHandle redisserver;
if (ERROR_SUCCESS != RegOpenKeyA(redis1, cRedisServer.c_str(), redisserver)) {
if (ERROR_SUCCESS != RegCreateKeyA(redis1, cRedisServer.c_str(), redisserver)) {
throw std::system_error(GetLastError(), system_category(), "RegCreateKeyA failed");
}
}
void InstallEventLogSource() {
const std::string keyPath(
"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\" + this->eventLogName);
HKEY key;
DWORD last_error = RegCreateKeyExA(
HKEY_LOCAL_MACHINE,
keyPath.c_str(),
0,
0,
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE,
0,
&key,
0);
char result[MAX_PATH];
GetModuleFileNameA(NULL, result, MAX_PATH);
std::string appPath = result;
if (ERROR_SUCCESS == last_error) {
DWORD last_error;
const DWORD types_supported = EVENTLOG_ERROR_TYPE |
EVENTLOG_WARNING_TYPE |
EVENTLOG_INFORMATION_TYPE;
last_error = RegSetValueExA(
key,
"EventMessageFile",
0,
REG_SZ,
(const BYTE *)appPath.c_str(),
(DWORD)(appPath.length()));
if (ERROR_SUCCESS == last_error) {
last_error = RegSetValueExA(key,
"TypesSupported",
0,
REG_DWORD,
(const BYTE*)&types_supported,
sizeof(types_supported));
}
if (ERROR_SUCCESS != last_error) {
std::cerr << "Failed to install source values: "
<< last_error << "\n";
}
RegCloseKey(key);
} else {
std::cerr << "Failed to install source: " << last_error << "\n";
DWORD value = 0;
DWORD type = REG_DWORD;
DWORD size = sizeof(DWORD);
if (ERROR_SUCCESS != RegQueryValueExA(redisserver, cTypesSupported.c_str(), 0, &type, NULL, &size)) {
if (ERROR_SUCCESS != RegSetValueExA(redisserver, cTypesSupported.c_str(), 0, REG_DWORD, (const BYTE*)&value, sizeof(DWORD))) {
throw std::system_error(GetLastError(), system_category(), "RegSetValueExA failed");
}
}
public:
void EnsureInitialization(LPCSTR eventLogName) {
if (this->eventLogName.length() == 0) {
this->eventLogName = eventLogName;
InstallEventLogSource();
type = REG_SZ;
size = 0;
if (ERROR_SUCCESS != RegQueryValueExA(redisserver, cEventMessageFile.c_str(), 0, &type, NULL, &size)) {
if (ERROR_SUCCESS != RegSetValueExA(redisserver, cEventMessageFile.c_str(), 0, REG_SZ, (BYTE*)appPath.c_str(), (DWORD)appPath.length())) {
throw std::system_error(GetLastError(), system_category(), "RegSetValueExA failed");
}
}
void LogMessageToEventLog(LPCSTR msg, const WORD type) {
DWORD eventID;
switch (type) {
case EVENTLOG_ERROR_TYPE:
eventID = MSG_ERROR_1;
break;
case EVENTLOG_WARNING_TYPE:
eventID = MSG_WARNING_1;
break;
case EVENTLOG_INFORMATION_TYPE:
eventID = MSG_INFO_1;
break;
default:
std::cerr << "Unrecognized type: " << type << "\n";
eventID = MSG_INFO_1;
break;
SmartRegistryHandle application;
if (ERROR_SUCCESS != RegOpenKeyA(eventLogKey, cApplication.c_str() , application)) {
throw std::system_error(GetLastError(), system_category(), "RegCreateKeyA failed");
}
SmartRegistryHandle redis2;
if (ERROR_SUCCESS != RegOpenKeyA(application, cRedis.c_str(), redis2)) {
if (ERROR_SUCCESS != RegCreateKeyA(application, cRedis.c_str(), redis2)) {
throw std::system_error(GetLastError(), system_category(), "RegCreateKeyA failed");
}
}
type = REG_DWORD;
size = 0;
if (ERROR_SUCCESS != RegQueryValueExA(redis2, cTypesSupported.c_str(), 0, &type, NULL, &size)) {
if (ERROR_SUCCESS != RegSetValueExA(redis2, cTypesSupported.c_str(), 0, REG_DWORD, (const BYTE*)&value, sizeof(DWORD))) {
throw std::system_error(GetLastError(), system_category(), "RegSetValueExA failed");
}
}
if (ERROR_SUCCESS != RegQueryValueExA(redis2, cEventMessageFile.c_str(), 0, &type, NULL, &size)) {
if (ERROR_SUCCESS != RegSetValueExA(redis2, cEventMessageFile.c_str(), 0, REG_SZ, (BYTE*)appPath.c_str(), (DWORD)appPath.length())) {
throw std::system_error(GetLastError(), system_category(), "RegSetValueExA failed");
}
}
}
HANDLE hEventLog = RegisterEventSourceA(0, this->eventLogName.c_str());
void RedisEventLog::LogMessageToEventLog(LPCSTR msg, const WORD type) {
DWORD eventID;
switch (type) {
case EVENTLOG_ERROR_TYPE:
eventID = MSG_ERROR_1;
break;
case EVENTLOG_WARNING_TYPE:
eventID = MSG_WARNING_1;
break;
case EVENTLOG_INFORMATION_TYPE:
eventID = MSG_INFO_1;
break;
default:
std::cerr << "Unrecognized type: " << type << "\n";
eventID = MSG_INFO_1;
break;
}
if (0 == hEventLog) {
std::cerr << "Failed open source '" << this->eventLogName << "': " << GetLastError() << endl;
} else {
if (FALSE == ReportEventA(
hEventLog,
type,
0,
eventID,
0,
1,
0,
&msg,
0)) {
std::cerr << "Failed to write message: " << GetLastError() << endl;
}
HANDLE hEventLog = RegisterEventSourceA(0, this->eventLogName.c_str());
DeregisterEventSource(hEventLog);
if (0 == hEventLog) {
std::cerr << "Failed open source '" << this->eventLogName << "': " << GetLastError() << endl;
} else {
if (FALSE == ReportEventA( hEventLog, type, 0, eventID, 0, 1, 0, &msg, 0)) {
std::cerr << "Failed to write message: " << GetLastError() << endl;
}
}
};
DeregisterEventSource(hEventLog);
}
}
extern "C" void LogToEventLog(const char* eventLogName, const char* msg) {
extern "C" void WriteEventLog(const char* sysLogInstance, const char* msg) {
try {
RedisEventLog::getInstance().EnsureInitialization(eventLogName);
RedisEventLog::getInstance().LogMessageToEventLog(msg, EVENTLOG_INFORMATION_TYPE);
stringstream ss;
ss << sysLogInstance << ":: " << msg;
RedisEventLog().LogMessageToEventLog(ss.str().c_str(),EVENTLOG_INFORMATION_TYPE);
} catch (...) {
}
}
#pragma once
#ifdef __cplusplus
#include <string>
using namespace std;
typedef class RedisEventLog {
public:
~RedisEventLog() {}
void InstallEventLogSource(string appPath);
void UninstallEventLogSource();
void LogMessageToEventLog(LPCSTR msg, const WORD type);
private:
const string eventLogName = "redis";
const string cEventLogPath = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
const string cEventLogApplicitonPath = cEventLogPath + "Application\\";
const string cRedis = "redis";
const string cEventMessageFile = "EventMessageFile";
const string cRedisServer = "redis-server";
const string cTypesSupported = "TypesSupported";
const string cApplication = "Application";
} RedisEventLog;
extern "C" {
#endif
void LogToEventLog(const char* logName, const char* msg);
void WriteEventLog(const char* sysLogInstance, const char* msg);
#ifdef __cplusplus
}
......
......@@ -49,9 +49,6 @@ rc.exe -foresources/EventLog.res resources/EventLog.rc</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EventLog.h</Outputs>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Resource Include="resources\EventLog.res" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{8C07F811-C81C-432C-B334-1AE6FAECF951}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
......
......@@ -342,4 +342,44 @@ public:
CloseServiceHandle(m_handle);
m_handle = NULL;
}
} SmartServiceHandle;
\ No newline at end of file
} SmartServiceHandle;
typedef class SmartRegistryHandle {
private:
HKEY m_handle;
public:
operator HKEY() {
return m_handle;
}
operator HKEY* () {
return &m_handle;
}
SmartRegistryHandle & operator= (const HKEY handle) {
m_handle = handle;
return *this;
}
SmartRegistryHandle() {
m_handle = NULL;
}
SmartRegistryHandle(const HKEY handle) {
m_handle = handle;
}
BOOL Valid() {
return (m_handle != NULL);
}
BOOL Invalid() {
return (m_handle == NULL);
}
~SmartRegistryHandle() {
RegCloseKey(m_handle);
m_handle = NULL;
}
} SmartRegistryHandle;
\ No newline at end of file
......@@ -56,7 +56,7 @@ This code implements the following new command line arguments for redis:
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include "Win32_EventLog.h"
#include <algorithm>
#include <string>
#include <sstream>
......@@ -72,10 +72,12 @@ using namespace std;
SERVICE_STATUS g_ServiceStatus = { 0 };
HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE;
HANDLE g_ServiceStoppedEvent = INVALID_HANDLE_VALUE;
vector<string> serviceRunArguments;
SERVICE_STATUS_HANDLE g_StatusHandle;
const ULONGLONG cThirtySeconds = 30 * 1000;
BOOL g_isRunningAsService = FALSE;
const int cPreshutdownInterval = 180000;
extern "C" int main(int argc, char** argv);
......@@ -120,6 +122,14 @@ VOID ServiceInstall(int argc, char ** argv) {
throw std::system_error(GetLastError(), system_category(), "CreateService failed");
}
SERVICE_PRESHUTDOWN_INFO preshutdownInfo;
preshutdownInfo.dwPreshutdownTimeout = cPreshutdownInterval;
if (FALSE == ChangeServiceConfig2(shService, SERVICE_CONFIG_PRESHUTDOWN_INFO, &preshutdownInfo)) {
throw std::system_error(GetLastError(), system_category(), "ChangeServiceConfig2 failed");
}
RedisEventLog().InstallEventLogSource(szPath);
cout << "Redis successfully installed as a service." << endl;
}
......@@ -139,6 +149,7 @@ VOID ServiceStart() {
throw std::system_error(GetLastError(), system_category(), "StartService failed");
}
// it will take atleast a couple of seconds for the service to start.
Sleep(2000);
......@@ -176,7 +187,7 @@ VOID ServiceStop() {
}
SERVICE_STATUS status;
if (FALSE == ControlService(shService, SERVICE_CONTROL_STOP, &status)) {
throw std::system_error(GetLastError(), system_category(), "ChangeServiceConfig failed");
throw std::system_error(GetLastError(), system_category(), "ControlService failed");
}
ULONGLONG start = GetTickCount64();
......@@ -202,12 +213,14 @@ VOID ServiceUninstall() {
throw std::system_error(GetLastError(), system_category(), "OpenSCManager failed");
}
shService = OpenServiceA(shSCManager, SERVICE_NAME, SERVICE_ALL_ACCESS);
if (shSCManager.Invalid()) {
throw std::system_error(GetLastError(), system_category(), "OpenService failed");
}
if (FALSE == DeleteService(shService)) {
throw std::system_error(GetLastError(), system_category(), "DeleteService failed");
if (shService.Valid()) {
if (FALSE == DeleteService(shService)) {
throw std::system_error(GetLastError(), system_category(), "DeleteService failed");
}
}
RedisEventLog().UninstallEventLogSource();
cout << "Redis service successfully uninstalled." << endl;
}
......@@ -253,6 +266,8 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) {
delete argv;
argv = nullptr;
SetEvent(g_ServiceStoppedEvent);
return ERROR_SUCCESS;
} catch (std::system_error syserr) {
stringstream err;
......@@ -269,12 +284,11 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) {
return ERROR_PROCESS_ABORTED;
}
VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode) {
switch (CtrlCode) {
case SERVICE_CONTROL_STOP:
if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
break;
DWORD WINAPI ServiceCtrlHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext) {
switch (dwControl) {
case SERVICE_CONTROL_PRESHUTDOWN:
{
SetEvent(g_ServiceStopEvent);
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
......@@ -285,20 +299,51 @@ VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode) {
throw std::system_error(GetLastError(), system_category(), "SetServiceStatus failed");
}
// This will signal the worker thread to start shutting down
SetEvent(g_ServiceStopEvent);
break;
}
case SERVICE_CONTROL_STOP:
{
DWORD start = GetTickCount();
while (GetTickCount() - start > cPreshutdownInterval) {
if (WaitForSingleObject(g_ServiceStoppedEvent, cPreshutdownInterval / 10) == WAIT_OBJECT_0) {
break;
}
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 4;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) {
throw std::system_error(GetLastError(), system_category(), "SetServiceStatus failed");
}
}
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 4;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) {
throw std::system_error(GetLastError(), system_category(), "SetServiceStatus failed");
}
break;
}
default:
{
break;
}
}
return NO_ERROR;
}
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
DWORD Status = E_FAIL;
g_StatusHandle = RegisterServiceCtrlHandlerA(SERVICE_NAME, ServiceCtrlHandler);
g_StatusHandle = RegisterServiceCtrlHandlerExA(SERVICE_NAME, ServiceCtrlHandler,NULL);
if (g_StatusHandle == NULL) {
return;
}
......@@ -315,6 +360,7 @@ VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
throw std::system_error(GetLastError(), system_category(), "SetServiceStatus failed");
}
g_ServiceStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_ServiceStopEvent == NULL) {
g_ServiceStatus.dwControlsAccepted = 0;
......@@ -329,7 +375,7 @@ VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
return;
}
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN;
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
......@@ -389,43 +435,40 @@ void BuildServiceRunArguments(int argc, char** argv) {
extern "C" BOOL HandleServiceCommands(int argc, char **argv) {
try {
if (argc > 1) {
string servicearg = argv[1];
std::transform(servicearg.begin(), servicearg.end(), servicearg.begin(), ::tolower);
if (servicearg == "--service-install") {
ServiceInstall(argc, argv);
return TRUE;
} else if (servicearg == "--service-uninstall") {
ServiceUninstall();
return TRUE;
} else if (servicearg == "--service-run") {
g_isRunningAsService = TRUE;
BuildServiceRunArguments(argc, argv);
ServiceRun();
return TRUE;
} else if (servicearg == "--service-start") {
ServiceStart();
return TRUE;
} else if (servicearg == "--service-stop") {
ServiceStop();
return TRUE;
}
string servicearg = argv[1];
std::transform(servicearg.begin(), servicearg.end(), servicearg.begin(), ::tolower);
if (servicearg == "--service-install") {
ServiceInstall(argc, argv);
return TRUE;
} else if (servicearg == "--service-uninstall") {
ServiceUninstall();
return TRUE;
} else if (servicearg == "--service-run") {
g_isRunningAsService = TRUE;
BuildServiceRunArguments(argc, argv);
ServiceRun();
return TRUE;
} else if (servicearg == "--service-start") {
ServiceStart();
return TRUE;
} else if (servicearg == "--service-stop") {
ServiceStop();
return TRUE;
}
}
// not a service command. start redis normally.
return FALSE;
} catch (std::system_error syserr) {
stringstream err;
err << "HandleServiceCommands: system error caught. error code=0x" << hex << syserr.code().value() << ", message = " << syserr.what() << endl;
OutputDebugStringA(err.str().c_str());
cout << "HandleServiceCommands: system error caught. error code=" << syserr.code().value() << ", message = " << syserr.what() << endl;
exit(1);
} catch (std::runtime_error runerr) {
stringstream err;
err << "HandleServiceCommands: runtime error caught. message=" << runerr.what() << endl;
cout << "HandleServiceCommands: runtime error caught. message=" << runerr.what() << endl;
OutputDebugStringA(err.str().c_str());
exit(1);
} catch (...) {
OutputDebugStringA("HandleServiceCommands: other exception caught.");
cout << "HandleServiceCommands: other exception caught." << endl;
exit(1);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册