diff --git a/bin/release/redis-2.8.12.zip b/bin/release/redis-2.8.12.zip
index f9d8a7d1..be7938f2 100644
Binary files a/bin/release/redis-2.8.12.zip and b/bin/release/redis-2.8.12.zip differ
diff --git a/msvs/setups/PullBinaries.ps1 b/msvs/setups/PullBinaries.ps1
index 6dd21fad..40e99639 100644
--- a/msvs/setups/PullBinaries.ps1
+++ b/msvs/setups/PullBinaries.ps1
@@ -2,7 +2,7 @@
$CurDir = split-path -parent $MyInvocation.MyCommand.Definition
-$SourceZip = [System.IO.Path]::Combine($CurDir, "..\..\bin\Release\redis-2.8.9.zip" )
+$SourceZip = [System.IO.Path]::Combine($CurDir, "..\..\bin\Release\redis-2.8.12.zip" )
$Destination = [System.IO.Path]::Combine($CurDir, "signed_binaries" )
[System.IO.Directory]::CreateDirectory($Destination) | Out-Null
diff --git a/msvs/setups/chocolatey/Redis.nuspec b/msvs/setups/chocolatey/Redis.nuspec
index 8015ac50..5da0ec87 100644
--- a/msvs/setups/chocolatey/Redis.nuspec
+++ b/msvs/setups/chocolatey/Redis.nuspec
@@ -3,7 +3,7 @@
redis-64
redis-64
- 2.8.9
+ 2.8.12
Jonathan Pickett
Microsoft Open Technologies, Inc.
Redis is a very popular open-source, networked, in-memory, key-value data store known for high performance, flexibility, a rich set of data structures, and a simple straightforward API.
@@ -14,7 +14,7 @@
https://github.com/MSOpenTech/redis/blob/2.8/license.txt
false
http://redis.io/images/redis.png
- Includes changes from redis 2.8.9. Please see the release notes for the UNIX 2.8 branch to understand how this impacts Redis functionality. This release adds support for running redis as a Windows Service. Please see RedisService.docx file for documentation.
+ Includes the changes from Redis 2.8.9 -> 2.8.12. Please see the release notes for the UNIX 2.8 branch to understand how this impacts Redis functionality.
diff --git a/msvs/setups/documentation/redis.windows.conf b/msvs/setups/documentation/redis.windows.conf
index 9a348c71..632c0351 100644
--- a/msvs/setups/documentation/redis.windows.conf
+++ b/msvs/setups/documentation/redis.windows.conf
@@ -137,7 +137,8 @@ dbfilename dump.rdb
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
-# The Append Only File will also be created inside this directory.
+# The Append Only File and the QFork memory mapped file will also be created
+# inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
diff --git a/msvs/setups/nuget/Redis.nuspec b/msvs/setups/nuget/Redis.nuspec
index 8015ac50..5da0ec87 100644
--- a/msvs/setups/nuget/Redis.nuspec
+++ b/msvs/setups/nuget/Redis.nuspec
@@ -3,7 +3,7 @@
redis-64
redis-64
- 2.8.9
+ 2.8.12
Jonathan Pickett
Microsoft Open Technologies, Inc.
Redis is a very popular open-source, networked, in-memory, key-value data store known for high performance, flexibility, a rich set of data structures, and a simple straightforward API.
@@ -14,7 +14,7 @@
https://github.com/MSOpenTech/redis/blob/2.8/license.txt
false
http://redis.io/images/redis.png
- Includes changes from redis 2.8.9. Please see the release notes for the UNIX 2.8 branch to understand how this impacts Redis functionality. This release adds support for running redis as a Windows Service. Please see RedisService.docx file for documentation.
+ Includes the changes from Redis 2.8.9 -> 2.8.12. Please see the release notes for the UNIX 2.8 branch to understand how this impacts Redis functionality.
diff --git a/src/Win32_Interop/Win32_CommandLine.cpp b/src/Win32_Interop/Win32_CommandLine.cpp
index 1470bd56..ce2d3787 100644
--- a/src/Win32_Interop/Win32_CommandLine.cpp
+++ b/src/Win32_Interop/Win32_CommandLine.cpp
@@ -32,6 +32,8 @@
// definition. #undef solves the warning messages.
#undef close
+#include
+
#include
#include
#include
@@ -40,22 +42,26 @@
#include
using namespace std;
+#pragma comment (lib, "Shlwapi.lib")
ArgumentMap g_argMap;
+vector g_pathsAccessed;
string stripQuotes(string s) {
- if (s.at(0) == '\'' && s.at(s.length() - 1) == '\'') {
- if (s.length() > 2) {
- return s.substr(1, s.length() - 2);
- } else {
- return string("");
+ if (s.length() >= 2) {
+ if (s.at(0) == '\'' && s.at(s.length() - 1) == '\'') {
+ if (s.length() > 2) {
+ return s.substr(1, s.length() - 2);
+ } else {
+ return string("");
+ }
}
- }
- if (s.at(0) == '\"' && s.at(s.length() - 1) == '\"') {
- if (s.length() > 2) {
- return s.substr(1, s.length() - 2);
- } else {
- return string("");
+ if (s.at(0) == '\"' && s.at(s.length() - 1) == '\"') {
+ if (s.length() > 2) {
+ return s.substr(1, s.length() - 2);
+ } else {
+ return string("");
+ }
}
}
return s;
@@ -376,7 +382,7 @@ static RedisParamterMapper g_redisArgMap =
{ "rdbcompression", &fp1 }, // rdbcompression [yes/no]
{ "rdbchecksum", &fp1 }, // rdbchecksum [yes/no]
{ "dbfilename", &fp1 }, // dbfilename [filename]
- { "dir", &fp1 }, // dir [path]
+ { cDir, &fp1 }, // dir [path]
{ "slaveof", &fp2 }, // slaveof [masterip] [master port]
{ "masterauth", &fp1 }, // masterauth [master-password]
{ "slave-serve-stale-data", &fp1 }, // slave-serve-stale-data [yes/no]
@@ -440,7 +446,58 @@ std::vector split(const std::string &s, char delim) {
return elems;
}
-void ParseConfFile(string confFile, ArgumentMap& argMap) {
+vector Tokenize(string line) {
+ vector tokens;
+ stringstream token;
+
+ // no need to parse empty lines, or comment lines (which may have unbalanced quotes)
+ if ((line.length() == 0) ||
+ ((line.length() != 0) && (*line.begin()) == '#')) {
+ return tokens;
+ }
+
+ for (string::const_iterator sit = line.begin(); sit != line.end(); sit++) {
+ char c = *(sit);
+ if (isspace(c) && token.str().length() > 0) {
+ tokens.push_back(token.str());
+ token.str("");
+ } else if (c == '\'' || c == '\"') {
+ char endQuote = c;
+ string::const_iterator endQuoteIt = sit;
+ while (++endQuoteIt != line.end()) {
+ if (*endQuoteIt == endQuote) break;
+ }
+ if (endQuoteIt != line.end()) {
+ while (++sit != endQuoteIt) {
+ token << (*sit);
+ }
+
+ // The code above strips quotes. In certain cases (save "") the quotes should be preserved around empty strings
+ if (token.str().length() == 0)
+ token << endQuote << endQuote;
+
+ // correct paths for windows nomenclature
+ string path = token.str();
+ replace(path.begin(), path.end(), '/', '\\');
+ tokens.push_back(path);
+
+ token.str("");
+ } else {
+ // stuff the imbalanced quote character and continue
+ token << (*sit);
+ }
+ } else {
+ token << c;
+ }
+ }
+ if (token.str().length() > 0) {
+ tokens.push_back(token.str());
+ }
+
+ return tokens;
+}
+
+void ParseConfFile(string confFile, string cwd, ArgumentMap& argMap) {
ifstream config;
string line;
string value;
@@ -448,25 +505,38 @@ void ParseConfFile(string confFile, ArgumentMap& argMap) {
#ifdef _DEBUG
cout << "processing " << confFile << endl;
#endif
+ char fullConfFilePath[MAX_PATH];
+ if (PathIsRelativeA(confFile.c_str())) {
+ if (NULL == PathCombineA(fullConfFilePath, cwd.c_str(), confFile.c_str())) {
+ throw std::system_error(GetLastError(), system_category(), "PathCombineA failed");
+ }
+ } else {
+ strcpy(fullConfFilePath, confFile.c_str());
+ }
- config.open(confFile);
+ config.open(fullConfFilePath);
if (config.fail()) {
stringstream ss;
- char buffer[MAX_PATH];
- ::GetCurrentDirectoryA(MAX_PATH, buffer);
- ss << "Failed to open the .conf file: " << confFile << " CWD=" << buffer;
+ ss << "Failed to open the .conf file: " << confFile << " CWD=" << cwd.c_str();
throw runtime_error(ss.str());
+ } else {
+ char confFileDir[MAX_PATH];
+ strcpy(confFileDir, fullConfFilePath);
+ if (FALSE == PathRemoveFileSpecA(confFileDir)) {
+ throw std::system_error(GetLastError(), system_category(), "PathRemoveFileSpecA failed");
+ }
+ g_pathsAccessed.push_back(confFileDir);
}
while (!config.eof()) {
getline(config, line);
- vector tokens = split(line, ' ');
+ vector tokens = Tokenize(line);
if (tokens.size() > 0) {
string parameter = tokens.at(0);
if (parameter.at(0) == '#') {
continue;
} else if (parameter.compare(cInclude) == 0) {
- ParseConfFile(tokens.at(1), argMap);
+ ParseConfFile(tokens.at(1), cwd, argMap);
} else if (g_redisArgMap.find(parameter) == g_redisArgMap.end()) {
stringstream err;
err << "unknown conf file parameter : " + parameter;
@@ -530,7 +600,27 @@ void ParseCommandLineArguments(int argc, char** argv) {
}
}
- if (confFile) ParseConfFile(confFilePath, g_argMap);
+ char cwd[MAX_PATH];
+ if (0 == ::GetCurrentDirectoryA(MAX_PATH, cwd)) {
+ throw std::system_error(GetLastError(), system_category(), "ParseCommandLineArguments: GetCurrentDirectoryA failed");
+ }
+
+ if (confFile) ParseConfFile(confFilePath, cwd, g_argMap);
+
+ // grab directory where RDB/AOF/DAT files will be created so that service install can add access allowed ACE to path
+ string fileCreationDirectory = ".\\";
+ if (g_argMap.find(cDir) != g_argMap.end()) {
+ fileCreationDirectory = g_argMap[cDir][0][0];
+ replace(fileCreationDirectory.begin(), fileCreationDirectory.end(), '/', '\\');
+ }
+ if (PathIsRelativeA(fileCreationDirectory.c_str())) {
+ char fullPath[MAX_PATH];
+ if (NULL == PathCombineA(fullPath, cwd, fileCreationDirectory.c_str())) {
+ throw std::system_error(GetLastError(), system_category(), "PathCombineA failed");
+ }
+ fileCreationDirectory = fullPath;
+ }
+ g_pathsAccessed.push_back(fileCreationDirectory);
#ifdef _DEBUG
cout << "arguments seen:" << endl;
@@ -552,4 +642,9 @@ void ParseCommandLineArguments(int argc, char** argv) {
}
}
#endif
-}
\ No newline at end of file
+}
+
+vector GetAccessPaths() {
+ return g_pathsAccessed;
+}
+
diff --git a/src/Win32_Interop/Win32_CommandLine.h b/src/Win32_Interop/Win32_CommandLine.h
index 11ff3742..548c5eb3 100644
--- a/src/Win32_Interop/Win32_CommandLine.h
+++ b/src/Win32_Interop/Win32_CommandLine.h
@@ -35,8 +35,9 @@ using namespace std;
typedef map>> ArgumentMap;
extern ArgumentMap g_argMap;
-void ParseConfFile(string confFile, ArgumentMap& argMap);
+void ParseConfFile(string confFile, string cwd, ArgumentMap& argMap);
void ParseCommandLineArguments(int argc, char** argv);
+vector GetAccessPaths();
const string cQFork = "qfork";
const string cServiceRun = "service-run";
@@ -49,6 +50,7 @@ const string cSyslogEnabled = "syslog-enabled";
const string cSyslogIdent= "syslog-ident";
const string cLogfile = "logfile";
const string cInclude = "include";
+const string cDir = "dir";
const string cMaxHeap = "maxheap";
const string cMaxMemory = "maxmemory";
diff --git a/src/Win32_Interop/Win32_FDAPI.cpp b/src/Win32_Interop/Win32_FDAPI.cpp
index 77a1e9aa..38bc1d85 100644
--- a/src/Win32_Interop/Win32_FDAPI.cpp
+++ b/src/Win32_Interop/Win32_FDAPI.cpp
@@ -1033,7 +1033,7 @@ BOOL ParseStorageAddress(const char *ip, int port, SOCKADDR_STORAGE* pSotrageAdd
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* Setting AI_PASSIVE will give you a wildcard address if addr is NULL */
- hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
+ hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE;
if ((status = getaddrinfo(ip, port_buffer, &hints, &res) != 0)) {
fprintf(stderr, "getaddrinfo: %S\n", gai_strerror(status));
diff --git a/src/Win32_Interop/Win32_QFork.cpp b/src/Win32_Interop/Win32_QFork.cpp
index 1b1ab0e6..91ecb978 100644
--- a/src/Win32_Interop/Win32_QFork.cpp
+++ b/src/Win32_Interop/Win32_QFork.cpp
@@ -42,6 +42,7 @@
#include
#include
#include
+#include
using namespace std;
const long long cSentinelHeapSize = 30 * 1024 * 1024;
@@ -136,7 +137,7 @@ How the parent invokes the QFork process:
const SIZE_T cAllocationGranularity = 1 << 18; // 256KB per heap block (matches large block allocation threshold of dlmalloc)
const int cMaxBlocks = 1 << 24; // 256KB * 16M heap blocks = 4TB. 4TB is the largest memory config Windows supports at present.
-const wchar_t* cMapFileBaseName = L"RedisQFork";
+const char* cMapFileBaseName = "RedisQFork";
const int cDeadForkWait = 30000;
size_t pageSize = 0;
@@ -206,8 +207,10 @@ bool ReportSpecialSystemErrors(int error) {
"\n"
"The Windows version of Redis allocates a large memory mapped file for sharing\n"
"the heap with the forked process used in persistence operations. This file\n"
- "will be created in the current working directory. Windows is reporting that\n"
- "there is insufficient disk space available for this file (Windows error 0x70).\n"
+ "will be created in the current working directory or the directory specified by\n"
+ "the 'dir' directive in the .conf file. Windows is reporting that there is \n"
+ "insufficient disk space available for this file (Windows error 0x70).\n"
+ "\n"
"You may fix this problem by either reducing the size of the Redis heap with\n"
"the --maxheap flag, or by starting redis from a working directory with\n"
"sufficient space available for the Redis heap. \n"
@@ -329,6 +332,18 @@ BOOL QForkSlaveInit(HANDLE QForkConrolMemoryMapHandle, DWORD ParentProcessID) {
return FALSE;
}
+string GetWorkingDirectory() {
+ string workingDir = ".\\";
+ if (g_argMap.find(cDir) != g_argMap.end()) {
+ workingDir = g_argMap[cDir][0][0];
+ }
+ std::replace(workingDir.begin(), workingDir.end(), '/', '\\');
+ if (workingDir.at(workingDir.length() - 1) != '\\') {
+ workingDir = workingDir.append("\\");
+ }
+ return workingDir;
+}
+
BOOL QForkMasterInit( __int64 maxheapBytes ) {
try {
// allocate file map for qfork control so it can be passed to the forked process
@@ -379,34 +394,38 @@ BOOL QForkMasterInit( __int64 maxheapBytes ) {
// FILE_FLAG_DELETE_ON_CLOSE will not clean up files in the case of a BSOD or power failure.
// Clean up anything we can to prevent excessive disk usage.
- wchar_t heapMemoryMapWildCard[MAX_PATH];
- WIN32_FIND_DATA fd;
- swprintf_s(
+ char heapMemoryMapWildCard[MAX_PATH];
+ WIN32_FIND_DATAA fd;
+ sprintf_s(
heapMemoryMapWildCard,
MAX_PATH,
- L"%s_*.dat",
+ "%s%s_*.dat",
+ GetWorkingDirectory().c_str(),
cMapFileBaseName);
- HANDLE hFind = FindFirstFile(heapMemoryMapWildCard, &fd);
+ HANDLE hFind = FindFirstFileA(heapMemoryMapWildCard, &fd);
while (hFind != INVALID_HANDLE_VALUE) {
// Failure likely means the file is in use by another redis instance.
- DeleteFile(fd.cFileName);
+ DeleteFileA(fd.cFileName);
- if (FALSE == FindNextFile(hFind, &fd)) {
+ if (FALSE == FindNextFileA(hFind, &fd)) {
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
}
- wchar_t heapMemoryMapPath[MAX_PATH];
- swprintf_s(
- heapMemoryMapPath,
- MAX_PATH,
- L"%s_%d.dat",
+ string workingDir = GetWorkingDirectory();
+
+ char heapMemoryMapPath[MAX_PATH];
+ sprintf_s(
+ heapMemoryMapPath,
+ MAX_PATH,
+ "%s%s_%d.dat",
+ workingDir.c_str(),
cMapFileBaseName,
GetCurrentProcessId());
g_pQForkControl->heapMemoryMapFile =
- CreateFileW(
+ CreateFileA(
heapMemoryMapPath,
GENERIC_READ | GENERIC_WRITE,
0,
diff --git a/src/Win32_Interop/Win32_service.cpp b/src/Win32_Interop/Win32_service.cpp
index 11766398..c0631bc6 100644
--- a/src/Win32_Interop/Win32_service.cpp
+++ b/src/Win32_Interop/Win32_service.cpp
@@ -90,18 +90,39 @@ const char* cServiceInstallPipeName = "\\\\.\\pipe\\redis-service-install";
extern "C" int main(int argc, char** argv);
-void WriteServiceInstallMessage(string message) {
- HANDLE pipe = CreateFileA(cServiceInstallPipeName, GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- if (pipe != INVALID_HANDLE_VALUE) {
- DWORD bytesWritten = 0;
- WriteFile(pipe, message.c_str(), (DWORD)message.length(), &bytesWritten, NULL);
- CloseHandle(pipe);
- } else {
- ::redisLog(REDIS_WARNING, message.c_str());
+typedef class ServicePipeWriter {
+public:
+ static ServicePipeWriter& getInstance() {
+ static ServicePipeWriter instance;
+ return instance;
}
-}
+
+private:
+ HANDLE pipe = INVALID_HANDLE_VALUE;
+ ServicePipeWriter() {
+ pipe = CreateFileA(cServiceInstallPipeName, GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ }
+ ServicePipeWriter(ServicePipeWriter const&);
+ void operator=(ServicePipeWriter const&);
+ ~ServicePipeWriter() {
+ if (pipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(pipe);
+ pipe = INVALID_HANDLE_VALUE;
+ }
+ }
+
+public:
+ void Write(string message) {
+ if (pipe != INVALID_HANDLE_VALUE) {
+ DWORD bytesWritten = 0;
+ WriteFile(pipe, message.c_str(), (DWORD)message.length(), &bytesWritten, NULL);
+ } else {
+ ::redisLog(REDIS_WARNING, message.c_str());
+ }
+ }
+} ServicePipeWriter;
BOOL RelaunchAsElevatedProcess(int argc, char** argv) {
// create pipe for launched process to communicate back on
@@ -118,7 +139,12 @@ BOOL RelaunchAsElevatedProcess(int argc, char** argv) {
} else {
paramString << " ";
}
- paramString << argv[n];
+ string arg = argv[n];
+ if (arg.find(' ') != string::npos) {
+ paramString << "\"" << arg << "\"";
+ } else {
+ paramString << arg;
+ }
}
CHAR params[32768];
memset(params, 0, 32768);
@@ -261,6 +287,7 @@ VOID ServiceInstall(int argc, char ** argv) {
if (GetModuleFileNameA(NULL, szPath, MAX_PATH) == 0) {
throw std::system_error(GetLastError(), system_category(), "ServiceInstall: GetModuleFileNameA failed");
}
+
stringstream args;
for (int a = 0; a < argc; a++) {
if (a == 0) {
@@ -271,7 +298,12 @@ VOID ServiceInstall(int argc, char ** argv) {
// replace --service-install argument with --service-run
args << "--" << cServiceRun;
} else {
- args << argv[a];
+ string arg = argv[a];
+ if (arg.find(' ') != arg.npos) {
+ args << "\"" << argv[a] << "\"";
+ } else {
+ args << argv[a];
+ }
}
}
}
@@ -280,6 +312,7 @@ VOID ServiceInstall(int argc, char ** argv) {
if (shSCManager.Invalid()) {
throw std::system_error(GetLastError(), system_category(), "OpenSCManager failed");
}
+
shService = CreateServiceA(
shSCManager,
g_serviceName,
@@ -304,12 +337,16 @@ VOID ServiceInstall(int argc, char ** argv) {
RedisEventLog().InstallEventLogSource(szPath);
- // make sure NT AUTHORITY\\NetworkService" has rights to the directory the service is installed in (for RDB write)
- string folder = szPath;
- folder = folder.substr(0, folder.rfind('\\'));
- SetAccessACLOnFolder(userName, folder);
-
- WriteServiceInstallMessage("Redis successfully installed as a service.");
+ // make sure NT AUTHORITY\\NetworkService" has rights to every directory where a files may be accessed (CONF,AOF,RDB,DAT)
+ stringstream aceMessage;
+ aceMessage << "Granting read/write access to 'NT AUTHORITY\\NetworkService' on: ";
+ for (auto folder : GetAccessPaths()) {
+ SetAccessACLOnFolder(userName, folder);
+ aceMessage << "\"" << folder.c_str() << "\" ";
+ }
+ ServicePipeWriter::getInstance().Write(aceMessage.str().c_str());
+
+ ServicePipeWriter::getInstance().Write("Redis successfully installed as a service.");
}
VOID ServiceStart(int argc, char ** argv) {
@@ -337,16 +374,16 @@ VOID ServiceStart(int argc, char ** argv) {
DWORD start = GetTickCount();
while (QueryServiceStatus(shService, &status) == TRUE) {
if (status.dwCurrentState == SERVICE_RUNNING) {
- WriteServiceInstallMessage("Redis service successfully started.");
+ ServicePipeWriter::getInstance().Write("Redis service successfully started.");
break;
} else if (status.dwCurrentState == SERVICE_STOPPED) {
- WriteServiceInstallMessage("Redis service failed to start.");
+ ServicePipeWriter::getInstance().Write("Redis service failed to start.");
break;
}
DWORD current = GetTickCount();
if (current - start >= cThirtySeconds) {
- WriteServiceInstallMessage("Redis service start timed out.");
+ ServicePipeWriter::getInstance().Write("Redis service start timed out.");
break;
}
}
@@ -375,12 +412,12 @@ VOID ServiceStop(int argc, char ** argv) {
DWORD start = GetTickCount();
while (QueryServiceStatus(shService, &status) == TRUE) {
if (status.dwCurrentState == SERVICE_STOPPED) {
- WriteServiceInstallMessage("Redis service successfully stopped.");
+ ServicePipeWriter::getInstance().Write("Redis service successfully stopped.");
break;
}
DWORD current = GetTickCount();
if (current - start >= cThirtySeconds) {
- WriteServiceInstallMessage("Redis service stop timed out.");
+ ServicePipeWriter::getInstance().Write("Redis service stop timed out.");
break;
}
}
@@ -405,7 +442,7 @@ VOID ServiceUninstall(int argc, char** argv) {
RedisEventLog().UninstallEventLogSource();
- WriteServiceInstallMessage("Redis service successfully uninstalled.");
+ ServicePipeWriter::getInstance().Write("Redis service successfully uninstalled.");
}
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) {
@@ -671,17 +708,17 @@ extern "C" BOOL HandleServiceCommands(int argc, char **argv) {
} catch (std::system_error syserr) {
stringstream ss;
ss << "HandleServiceCommands: system error caught. error code=" << syserr.code().value() << ", message = " << syserr.what() << endl;
- WriteServiceInstallMessage(ss.str());
+ ServicePipeWriter::getInstance().Write(ss.str());
exit(1);
} catch (std::runtime_error runerr) {
stringstream err;
err << "HandleServiceCommands: runtime error caught. message=" << runerr.what() << endl;
- WriteServiceInstallMessage(err.str());
+ ServicePipeWriter::getInstance().Write(err.str());
exit(1);
} catch (...) {
stringstream ss;
ss << "HandleServiceCommands: other exception caught." << endl;
- WriteServiceInstallMessage(ss.str());
+ ServicePipeWriter::getInstance().Write(ss.str());
exit(1);
}
}
diff --git a/src/anet.c b/src/anet.c
index bc9b0939..b9bcd404 100644
--- a/src/anet.c
+++ b/src/anet.c
@@ -49,6 +49,7 @@
#ifdef _WIN32
#include "win32_Interop/win32fixes.h"
#define ANET_NOTUSED(V) V
+#include
#endif
#include "anet.h"
@@ -81,20 +82,34 @@ int anetNonBlock(char *err, int fd)
return ANET_OK;
}
-/* Set TCP keep alive option to detect dead peers. The interval option
- * is only used for Linux as we are using Linux-specific APIs to set
- * the probe send time, interval, and count. */
-int anetKeepAlive(char *err, int fd, int interval)
-{
+/* Set TCP keep alive option to detect dead peers. */
+int anetKeepAlive(char *err, int fd, int interval) {
+#ifdef _WIN32
int val = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)
- {
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1) {
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
return ANET_ERR;
}
-#ifdef __linux__
+ struct tcp_keepalive alive;
+ DWORD dwBytesRet = 0;
+ alive.onoff = TRUE;
+ alive.keepalivetime = interval * 1000;
+ /* According to http://msdn.microsoft.com/en-us/library/windows/desktop/ee470551(v=vs.85).aspx
+ On Windows Vista and later, the number of keep-alive probes (data retransmissions) is set to 10 and cannot be changed.
+ So we set the keep alive interval as interval/10, as 10 probes will be send before
+ detecting an error
+ */
+ val = interval/10;
+ if (val == 0) val = 1;
+ alive.keepaliveinterval = val*1000;
+ if(WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
+ NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) {
+ anetSetError(err, "WSAIotcl(SIO_KEEPALIVE_VALS) failed with error code %d\n", WSAGetLastError());
+ return ANET_ERR;
+ }
+#else
/* Default settings are more or less garbage, with the keepalive time
* set to 7200 by default on Linux. Modify settings to make the feature
* actually useful. */