summaryrefslogtreecommitdiff
path: root/lib/server
diff options
context:
space:
mode:
Diffstat (limited to 'lib/server')
-rw-r--r--lib/server/Daemon.cpp328
-rw-r--r--lib/server/Daemon.h28
-rw-r--r--lib/server/OverlappedIO.h42
-rw-r--r--lib/server/Protocol.cpp23
-rw-r--r--lib/server/ProtocolUncertainStream.cpp21
-rw-r--r--lib/server/SSLLib.cpp44
-rw-r--r--lib/server/SSLLib.h4
-rw-r--r--lib/server/ServerControl.cpp227
-rw-r--r--lib/server/ServerControl.h188
-rw-r--r--lib/server/ServerStream.h22
-rw-r--r--lib/server/ServerTLS.h12
-rw-r--r--lib/server/Socket.cpp15
-rw-r--r--lib/server/Socket.h6
-rw-r--r--lib/server/SocketListen.h85
-rw-r--r--lib/server/SocketStream.cpp101
-rw-r--r--lib/server/SocketStream.h3
-rw-r--r--lib/server/SocketStreamTLS.cpp21
-rw-r--r--lib/server/SocketStreamTLS.h3
-rw-r--r--lib/server/TLSContext.cpp14
-rw-r--r--lib/server/WinNamedPipeListener.h232
-rw-r--r--lib/server/WinNamedPipeStream.cpp153
-rw-r--r--lib/server/WinNamedPipeStream.h7
-rwxr-xr-xlib/server/makeprotocol.pl.in161
23 files changed, 1219 insertions, 521 deletions
diff --git a/lib/server/Daemon.cpp b/lib/server/Daemon.cpp
index bdbbb433..d868774f 100644
--- a/lib/server/Daemon.cpp
+++ b/lib/server/Daemon.cpp
@@ -47,22 +47,21 @@ Daemon *Daemon::spDaemon = 0;
//
// --------------------------------------------------------------------------
Daemon::Daemon()
- : mpConfiguration(NULL),
- mReloadConfigWanted(false),
+ : mReloadConfigWanted(false),
mTerminateWanted(false),
+ #ifdef WIN32
+ mSingleProcess(true),
+ mRunInForeground(true),
+ mKeepConsoleOpenAfterFork(true),
+ #else
mSingleProcess(false),
mRunInForeground(false),
mKeepConsoleOpenAfterFork(false),
+ #endif
mHaveConfigFile(false),
mAppName(DaemonName())
{
- if(spDaemon != NULL)
- {
- THROW_EXCEPTION(ServerException, AlreadyDaemonConstructed)
- }
- spDaemon = this;
-
- // And in debug builds, we'll switch on assert failure logging to syslog
+ // In debug builds, switch on assert failure logging to syslog
ASSERT_FAILS_TO_SYSLOG_ON
// And trace goes to syslog too
TRACE_TO_SYSLOG(true)
@@ -78,14 +77,6 @@ Daemon::Daemon()
// --------------------------------------------------------------------------
Daemon::~Daemon()
{
- if(mpConfiguration)
- {
- delete mpConfiguration;
- mpConfiguration = 0;
- }
-
- ASSERT(spDaemon == this);
- spDaemon = NULL;
}
// --------------------------------------------------------------------------
@@ -104,9 +95,9 @@ std::string Daemon::GetOptionString()
{
return "c:"
#ifndef WIN32
- "DFk"
+ "DFK"
#endif
- "hqvVt:T";
+ "hkPqQt:TUvVW:";
}
void Daemon::Usage()
@@ -123,13 +114,20 @@ void Daemon::Usage()
#ifndef WIN32
" -D Debugging mode, do not fork, one process only, one client only\n"
" -F Do not fork into background, but fork to serve multiple clients\n"
+#endif
" -k Keep console open after fork, keep writing log messages to it\n"
+#ifndef WIN32
+ " -K Stop writing log messages to console while daemon is running\n"
+ " -P Show process ID (PID) in console output\n"
#endif
" -q Run more quietly, reduce verbosity level by one, can repeat\n"
+ " -Q Run at minimum verbosity, log nothing\n"
" -v Run more verbosely, increase verbosity level by one, can repeat\n"
- " -V Run at maximum verbosity\n"
+ " -V Run at maximum verbosity, log everything\n"
+ " -W <level> Set verbosity to error/warning/notice/info/trace/everything\n"
" -t <tag> Tag console output with specified marker\n"
- " -T Timestamp console output\n";
+ " -T Timestamp console output\n"
+ " -U Timestamp console output with microseconds\n";
}
// --------------------------------------------------------------------------
@@ -166,13 +164,19 @@ int Daemon::ProcessOption(signed int option)
mRunInForeground = true;
}
break;
+#endif // !WIN32
case 'k':
{
mKeepConsoleOpenAfterFork = true;
}
break;
-#endif
+
+ case 'K':
+ {
+ mKeepConsoleOpenAfterFork = false;
+ }
+ break;
case 'h':
{
@@ -181,6 +185,12 @@ int Daemon::ProcessOption(signed int option)
}
break;
+ case 'P':
+ {
+ Console::SetShowPID(true);
+ }
+ break;
+
case 'q':
{
if(mLogLevel == Log::NOTHING)
@@ -194,6 +204,13 @@ int Daemon::ProcessOption(signed int option)
}
break;
+ case 'Q':
+ {
+ mLogLevel = Log::NOTHING;
+ }
+ break;
+
+
case 'v':
{
if(mLogLevel == Log::EVERYTHING)
@@ -213,9 +230,21 @@ int Daemon::ProcessOption(signed int option)
}
break;
+ case 'W':
+ {
+ mLogLevel = Logging::GetNamedLevel(optarg);
+ if (mLogLevel == Log::INVALID)
+ {
+ BOX_FATAL("Invalid logging level");
+ return 2;
+ }
+ }
+ break;
+
case 't':
{
- Console::SetTag(optarg);
+ Logging::SetProgramName(optarg);
+ Console::SetShowTag(true);
}
break;
@@ -225,6 +254,13 @@ int Daemon::ProcessOption(signed int option)
}
break;
+ case 'U':
+ {
+ Console::SetShowTime(true);
+ Console::SetShowTimeMicros(true);
+ }
+ break;
+
case '?':
{
BOX_FATAL("Unknown option on command line: "
@@ -260,7 +296,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
mConfigFileName = DefaultConfigFile;
mAppName = argv[0];
- #ifdef NDEBUG
+ #ifdef BOX_RELEASE_BUILD
mLogLevel = Log::NOTICE; // need an int to do math with
#else
mLogLevel = Log::INFO; // need an int to do math with
@@ -277,7 +313,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// reset getopt, just in case anybody used it before.
// unfortunately glibc and BSD differ on this point!
// http://www.ussg.iu.edu/hypermail/linux/kernel/0305.3/0262.html
- #if HAVE_DECL_OPTRESET == 1
+ #if HAVE_DECL_OPTRESET == 1 || defined WIN32
optind = 1;
optreset = 1;
#elif defined __GLIBC__
@@ -314,7 +350,8 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
return 2;
}
- Logging::SetGlobalLevel((Log::Level)mLogLevel);
+ Logging::FilterConsole((Log::Level)mLogLevel);
+ Logging::FilterSyslog ((Log::Level)mLogLevel);
return Main(mConfigFileName);
}
@@ -322,6 +359,98 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// --------------------------------------------------------------------------
//
// Function
+// Name: Daemon::Configure(const std::string& rConfigFileName)
+// Purpose: Loads daemon configuration. Useful when you have
+// a local Daemon object and don't intend to fork()
+// or call Main().
+// Created: 2008/04/19
+//
+// --------------------------------------------------------------------------
+
+bool Daemon::Configure(const std::string& rConfigFileName)
+{
+ // Load the configuration file.
+ std::string errors;
+ std::auto_ptr<Configuration> apConfig;
+
+ try
+ {
+ apConfig = Configuration::LoadAndVerify(rConfigFileName,
+ GetConfigVerify(), errors);
+ }
+ catch(BoxException &e)
+ {
+ if(e.GetType() == CommonException::ExceptionType &&
+ e.GetSubType() == CommonException::OSFileOpenError)
+ {
+ BOX_ERROR("Failed to open configuration file: " <<
+ rConfigFileName);
+ return false;
+ }
+
+ throw;
+ }
+
+ // Got errors?
+ if(apConfig.get() == 0)
+ {
+ BOX_ERROR("Failed to load or verify configuration file");
+ return false;
+ }
+
+ if(!Configure(*apConfig))
+ {
+ BOX_ERROR("Failed to verify configuration file");
+ return false;
+ }
+
+ // Store configuration
+ mConfigFileName = rConfigFileName;
+ mLoadedConfigModifiedTime = GetConfigFileModifiedTime();
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Daemon::Configure(const Configuration& rConfig)
+// Purpose: Loads daemon configuration. Useful when you have
+// a local Daemon object and don't intend to fork()
+// or call Main().
+// Created: 2008/08/12
+//
+// --------------------------------------------------------------------------
+
+bool Daemon::Configure(const Configuration& rConfig)
+{
+ std::string errors;
+
+ // Verify() may modify the configuration, e.g. adding default values
+ // for required keys, so need to make a copy here
+ std::auto_ptr<Configuration> apConf(new Configuration(rConfig));
+ apConf->Verify(*GetConfigVerify(), errors);
+
+ // Got errors?
+ if(!errors.empty())
+ {
+ BOX_ERROR("Configuration errors: " << errors);
+ return false;
+ }
+
+ // Store configuration
+ mapConfiguration = apConf;
+
+ // Let the derived class have a go at setting up stuff
+ // in the initial process
+ SetupInInitialProcess();
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
// Name: Daemon::Main(const std::string& rConfigFileName)
// Purpose: Starts the daemon off -- equivalent of C main() function
// Created: 2003/07/29
@@ -331,69 +460,38 @@ int Daemon::Main(const std::string &rConfigFileName)
{
// Banner (optional)
{
- #ifndef NDEBUG
- BOX_NOTICE(DaemonBanner());
- #endif
+ BOX_SYSLOG(Log::NOTICE, DaemonBanner());
}
std::string pidFileName;
- mConfigFileName = rConfigFileName;
-
- bool asDaemon = !mSingleProcess && !mRunInForeground;
+ bool asDaemon = !mSingleProcess && !mRunInForeground;
try
{
- // Load the configuration file.
- std::string errors;
- std::auto_ptr<Configuration> pconfig;
-
- try
- {
- pconfig = Configuration::LoadAndVerify(
- mConfigFileName.c_str(),
- GetConfigVerify(), errors);
- }
- catch(BoxException &e)
- {
- if(e.GetType() == CommonException::ExceptionType &&
- e.GetSubType() == CommonException::OSFileOpenError)
- {
- BOX_FATAL("Failed to start: failed to open "
- "configuration file: "
- << mConfigFileName);
- return 1;
- }
-
- throw;
- }
-
- // Got errors?
- if(pconfig.get() == 0 || !errors.empty())
+ if (!Configure(rConfigFileName))
{
- // Tell user about errors
- BOX_FATAL("Failed to start: errors in configuration "
- "file: " << mConfigFileName << ": " << errors);
- // And give up
+ BOX_FATAL("Failed to start: failed to load "
+ "configuration file: " << rConfigFileName);
return 1;
}
- // Store configuration
- mpConfiguration = pconfig.release();
- mLoadedConfigModifiedTime = GetConfigFileModifiedTime();
-
- // Let the derived class have a go at setting up stuff in the initial process
- SetupInInitialProcess();
-
// Server configuration
const Configuration &serverConfig(
- mpConfiguration->GetSubConfiguration("Server"));
+ mapConfiguration->GetSubConfiguration("Server"));
+
+ if(serverConfig.KeyExists("LogFacility"))
+ {
+ std::string facility =
+ serverConfig.GetKeyValue("LogFacility");
+ Logging::SetFacility(Syslog::GetNamedFacility(facility));
+ }
// Open PID file for writing
pidFileName = serverConfig.GetKeyValue("PidFile");
FileHandleGuard<(O_WRONLY | O_CREAT | O_TRUNC), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)> pidFile(pidFileName.c_str());
-#ifndef WIN32
+#ifndef WIN32
// Handle changing to a different user
if(serverConfig.KeyExists("User"))
{
@@ -409,7 +507,7 @@ int Daemon::Main(const std::string &rConfigFileName)
// Change the process ID
daemonUser.ChangeProcessUser();
}
-
+
if(asDaemon)
{
// Let's go... Daemonise...
@@ -436,8 +534,7 @@ int Daemon::Main(const std::string &rConfigFileName)
// Set new session
if(::setsid() == -1)
{
- BOX_ERROR("Failed to setsid(): " <<
- strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to setsid()");
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
@@ -446,6 +543,7 @@ int Daemon::Main(const std::string &rConfigFileName)
{
case -1:
// error
+ BOX_LOG_SYS_ERROR("Failed to fork() a child");
THROW_EXCEPTION(ServerException, DaemoniseFailed)
break;
@@ -460,7 +558,17 @@ int Daemon::Main(const std::string &rConfigFileName)
break;
}
}
-
+#endif // !WIN32
+
+ // Must set spDaemon before installing signal handler,
+ // otherwise the handler will crash if invoked too soon.
+ if(spDaemon != NULL)
+ {
+ THROW_EXCEPTION(ServerException, AlreadyDaemonConstructed)
+ }
+ spDaemon = this;
+
+#ifndef WIN32
// Set signal handler
// Don't do this in the parent, since it might be anything
// (e.g. test/bbackupd)
@@ -468,29 +576,24 @@ int Daemon::Main(const std::string &rConfigFileName)
struct sigaction sa;
sa.sa_handler = SignalHandler;
sa.sa_flags = 0;
- sigemptyset(&sa.sa_mask); // macro
- if(::sigaction(SIGHUP, &sa, NULL) != 0 || ::sigaction(SIGTERM, &sa, NULL) != 0)
+ sigemptyset(&sa.sa_mask); // macro
+ if(::sigaction(SIGHUP, &sa, NULL) != 0 ||
+ ::sigaction(SIGTERM, &sa, NULL) != 0)
{
+ BOX_LOG_SYS_ERROR("Failed to set signal handlers");
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
#endif // !WIN32
- // Log the start message
- BOX_NOTICE("Starting daemon, version " << BOX_VERSION
- << ", config: " << mConfigFileName);
-
// Write PID to file
char pid[32];
-#ifdef WIN32
- int pidsize = sprintf(pid, "%d", (int)GetCurrentProcessId());
-#else
int pidsize = sprintf(pid, "%d", (int)getpid());
-#endif
if(::write(pidFile, pid, pidsize) != pidsize)
{
- BOX_FATAL("can't write pid file");
+ BOX_LOG_SYS_FATAL("Failed to write PID file: " <<
+ pidFileName);
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
@@ -515,6 +618,7 @@ int Daemon::Main(const std::string &rConfigFileName)
int devnull = ::open(PLATFORM_DEV_NULL, O_RDWR, 0);
if(devnull == -1)
{
+ BOX_LOG_SYS_ERROR("Failed to open /dev/null");
THROW_EXCEPTION(CommonException, OSFileError);
}
// Then duplicate them to all three handles
@@ -530,9 +634,13 @@ int Daemon::Main(const std::string &rConfigFileName)
// And definitely don't try and send anything to those file descriptors
// -- this has in the past sent text to something which isn't expecting it.
TRACE_TO_STDOUT(false);
- Logging::ToConsole(false);
#endif // ! WIN32
- }
+ Logging::ToConsole(false);
+ }
+
+ // Log the start message
+ BOX_NOTICE("Starting daemon, version: " << BOX_VERSION);
+ BOX_NOTICE("Using configuration file: " << mConfigFileName);
}
catch(BoxException &e)
{
@@ -581,10 +689,10 @@ int Daemon::Main(const std::string &rConfigFileName)
BOX_NOTICE("Reloading configuration file: "
<< mConfigFileName);
std::string errors;
- std::auto_ptr<Configuration> pconfig =
+ std::auto_ptr<Configuration> pconfig(
Configuration::LoadAndVerify(
mConfigFileName.c_str(),
- GetConfigVerify(), errors);
+ GetConfigVerify(), errors));
// Got errors?
if(pconfig.get() == 0 || !errors.empty())
@@ -598,12 +706,8 @@ int Daemon::Main(const std::string &rConfigFileName)
break;
}
- // delete old configuration
- delete mpConfiguration;
- mpConfiguration = 0;
-
// Store configuration
- mpConfiguration = pconfig.release();
+ mapConfiguration = pconfig;
mLoadedConfigModifiedTime =
GetConfigFileModifiedTime();
@@ -638,8 +742,21 @@ int Daemon::Main(const std::string &rConfigFileName)
#ifdef WIN32
WSACleanup();
+#else
+ // Should clean up here, but it breaks memory leak tests.
+ /*
+ if(asDaemon)
+ {
+ // we are running in the child by now, and should not return
+ mapConfiguration.reset();
+ exit(0);
+ }
+ */
#endif
-
+
+ ASSERT(spDaemon == this);
+ spDaemon = NULL;
+
return retcode;
}
@@ -647,7 +764,8 @@ int Daemon::Main(const std::string &rConfigFileName)
//
// Function
// Name: Daemon::EnterChild()
-// Purpose: Sets up for a child task of the main server. Call just after fork()
+// Purpose: Sets up for a child task of the main server. Call
+// just after fork().
// Created: 2003/07/31
//
// --------------------------------------------------------------------------
@@ -789,13 +907,13 @@ const ConfigurationVerify *Daemon::GetConfigVerify() const
// --------------------------------------------------------------------------
const Configuration &Daemon::GetConfiguration() const
{
- if(mpConfiguration == 0)
+ if(mapConfiguration.get() == 0)
{
// Shouldn't get anywhere near this if a configuration file can't be loaded
THROW_EXCEPTION(ServerException, Internal)
}
- return *mpConfiguration;
+ return *mapConfiguration;
}
@@ -803,8 +921,10 @@ const Configuration &Daemon::GetConfiguration() const
//
// Function
// Name: Daemon::SetupInInitialProcess()
-// Purpose: A chance for the daemon to do something initial setting up in the process which
-// initiates everything, and after the configuration file has been read and verified.
+// Purpose: A chance for the daemon to do something initial
+// setting up in the process which initiates
+// everything, and after the configuration file has
+// been read and verified.
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
@@ -849,14 +969,16 @@ void Daemon::SetProcessTitle(const char *format, ...)
box_time_t Daemon::GetConfigFileModifiedTime() const
{
- struct stat st;
+ EMU_STRUCT_STAT st;
- if(::stat(GetConfigFileName().c_str(), &st) != 0)
+ if(EMU_STAT(GetConfigFileName().c_str(), &st) != 0)
{
if (errno == ENOENT)
{
return 0;
}
+ BOX_LOG_SYS_ERROR("Failed to stat configuration file: " <<
+ GetConfigFileName());
THROW_EXCEPTION(CommonException, OSFileError)
}
diff --git a/lib/server/Daemon.h b/lib/server/Daemon.h
index 482f926e..a3212a00 100644
--- a/lib/server/Daemon.h
+++ b/lib/server/Daemon.h
@@ -19,8 +19,8 @@
#include <string>
#include "BoxTime.h"
+#include "Configuration.h"
-class Configuration;
class ConfigurationVerify;
// --------------------------------------------------------------------------
@@ -40,7 +40,8 @@ private:
Daemon(const Daemon &rToCopy);
public:
- int Main(const char *DefaultConfigFile, int argc, const char *argv[]);
+ virtual int Main(const char *DefaultConfigFile, int argc,
+ const char *argv[]);
/* override this Main() if you want custom option processing: */
virtual int Main(const std::string &rConfigFile);
@@ -53,7 +54,10 @@ public:
virtual std::string DaemonBanner() const;
virtual const ConfigurationVerify *GetConfigVerify() const;
virtual void Usage();
-
+
+ virtual bool Configure(const std::string& rConfigFileName);
+ virtual bool Configure(const Configuration& rConfig);
+
bool StopRun() {return mReloadConfigWanted | mTerminateWanted;}
bool IsReloadConfigWanted() {return mReloadConfigWanted;}
bool IsTerminateWanted() {return mTerminateWanted;}
@@ -62,12 +66,20 @@ public:
void SetReloadConfigWanted() {mReloadConfigWanted = true;}
void SetTerminateWanted() {mTerminateWanted = true;}
- virtual void SetupInInitialProcess();
virtual void EnterChild();
static void SetProcessTitle(const char *format, ...);
+ void SetRunInForeground(bool foreground)
+ {
+ mRunInForeground = foreground;
+ }
+ void SetSingleProcess(bool value)
+ {
+ mSingleProcess = value;
+ }
protected:
+ virtual void SetupInInitialProcess();
box_time_t GetLoadedConfigModifiedTime() const;
bool IsSingleProcess() { return mSingleProcess; }
virtual std::string GetOptionString();
@@ -78,7 +90,7 @@ private:
box_time_t GetConfigFileModifiedTime() const;
std::string mConfigFileName;
- Configuration *mpConfiguration;
+ std::auto_ptr<Configuration> mapConfiguration;
box_time_t mLoadedConfigModifiedTime;
bool mReloadConfigWanted;
bool mTerminateWanted;
@@ -91,8 +103,10 @@ private:
std::string mAppName;
};
-#define DAEMON_VERIFY_SERVER_KEYS {"PidFile", 0, ConfigTest_Exists, 0}, \
- {"User", 0, ConfigTest_LastEntry, 0}
+#define DAEMON_VERIFY_SERVER_KEYS \
+ ConfigurationVerifyKey("PidFile", ConfigTest_Exists), \
+ ConfigurationVerifyKey("LogFacility", 0), \
+ ConfigurationVerifyKey("User", ConfigTest_LastEntry)
#endif // DAEMON__H
diff --git a/lib/server/OverlappedIO.h b/lib/server/OverlappedIO.h
new file mode 100644
index 00000000..12495053
--- /dev/null
+++ b/lib/server/OverlappedIO.h
@@ -0,0 +1,42 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: OverlappedIO.h
+// Purpose: Windows overlapped IO handle guard
+// Created: 2008/09/30
+//
+// --------------------------------------------------------------------------
+
+#ifndef OVERLAPPEDIO__H
+#define OVERLAPPEDIO__H
+
+class OverlappedIO
+{
+public:
+ OVERLAPPED mOverlapped;
+
+ OverlappedIO()
+ {
+ ZeroMemory(&mOverlapped, sizeof(mOverlapped));
+ mOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE,
+ NULL);
+ if (mOverlapped.hEvent == INVALID_HANDLE_VALUE)
+ {
+ BOX_LOG_WIN_ERROR("Failed to create event for "
+ "overlapped I/O");
+ THROW_EXCEPTION(ServerException, BadSocketHandle);
+ }
+ }
+
+ ~OverlappedIO()
+ {
+ if (CloseHandle(mOverlapped.hEvent) != TRUE)
+ {
+ BOX_LOG_WIN_ERROR("Failed to delete event for "
+ "overlapped I/O");
+ THROW_EXCEPTION(ServerException, BadSocketHandle);
+ }
+ }
+};
+
+#endif // !OVERLAPPEDIO__H
diff --git a/lib/server/Protocol.cpp b/lib/server/Protocol.cpp
index 4398a58f..5dc5d0b1 100644
--- a/lib/server/Protocol.cpp
+++ b/lib/server/Protocol.cpp
@@ -26,7 +26,7 @@
#include "MemLeakFindOn.h"
-#ifdef NDEBUG
+#ifdef BOX_RELEASE_BUILD
#define PROTOCOL_ALLOCATE_SEND_BLOCK_CHUNK 1024
#else
// #define PROTOCOL_ALLOCATE_SEND_BLOCK_CHUNK 1024
@@ -670,9 +670,18 @@ std::auto_ptr<IOStream> Protocol::ReceiveStream()
InformStreamReceiving(streamSize);
// Return a stream object
- return std::auto_ptr<IOStream>((streamSize == ProtocolStream_SizeUncertain)?
- ((IOStream*)(new ProtocolUncertainStream(mrStream)))
- :((IOStream*)(new PartialReadStream(mrStream, streamSize))));
+ if(streamSize == ProtocolStream_SizeUncertain)
+ {
+ BOX_TRACE("Receiving stream, size uncertain");
+ return std::auto_ptr<IOStream>(
+ new ProtocolUncertainStream(mrStream));
+ }
+ else
+ {
+ BOX_TRACE("Receiving stream, size " << streamSize << " bytes");
+ return std::auto_ptr<IOStream>(
+ new PartialReadStream(mrStream, streamSize));
+ }
}
// --------------------------------------------------------------------------
@@ -749,8 +758,10 @@ void Protocol::SendStream(IOStream &rStream)
}
// Send final byte to finish the stream
+ BOX_TRACE("Sending end of stream byte");
uint8_t endOfStream = ProtocolStreamHeader_EndOfStream;
mrStream.Write(&endOfStream, 1);
+ BOX_TRACE("Sent end of stream byte");
}
catch(...)
{
@@ -788,6 +799,7 @@ int Protocol::SendStreamSendBlock(uint8_t *Block, int BytesInBlock)
// Quick sanity check
if(BytesInBlock == 0)
{
+ BOX_TRACE("Zero size block, not sending anything");
return 0;
}
@@ -813,6 +825,8 @@ int Protocol::SendStreamSendBlock(uint8_t *Block, int BytesInBlock)
}
}
ASSERT(header > 0);
+ BOX_TRACE("Sending header byte " << (int)header << " plus " <<
+ writeSize << " bytes to stream");
// Store the header
Block[-1] = header;
@@ -820,6 +834,7 @@ int Protocol::SendStreamSendBlock(uint8_t *Block, int BytesInBlock)
// Write everything out
mrStream.Write(Block - 1, writeSize + 1);
+ BOX_TRACE("Sent " << (writeSize+1) << " bytes to stream");
// move the remainer to the beginning of the block for the next time round
if(writeSize != BytesInBlock)
{
diff --git a/lib/server/ProtocolUncertainStream.cpp b/lib/server/ProtocolUncertainStream.cpp
index 60c1fa1d..84a213a8 100644
--- a/lib/server/ProtocolUncertainStream.cpp
+++ b/lib/server/ProtocolUncertainStream.cpp
@@ -41,7 +41,8 @@ ProtocolUncertainStream::~ProtocolUncertainStream()
{
if(!mFinished)
{
- TRACE0("ProtocolUncertainStream::~ProtocolUncertainStream() destroyed when stream not complete\n");
+ BOX_WARNING("ProtocolUncertainStream destroyed before "
+ "stream finished");
}
}
@@ -76,11 +77,15 @@ int ProtocolUncertainStream::Read(void *pBuffer, int NBytes, int Timeout)
toRead = mBytesLeftInCurrentBlock;
}
+ BOX_TRACE("Reading " << toRead << " bytes from stream");
+
// Read it
int r = mrSource.Read(((uint8_t*)pBuffer) + read, toRead, Timeout);
// Give up now if it didn't return anything
if(r == 0)
{
+ BOX_TRACE("Read " << r << " bytes from "
+ "stream, returning");
return read;
}
@@ -91,16 +96,22 @@ int ProtocolUncertainStream::Read(void *pBuffer, int NBytes, int Timeout)
// stop now if the stream returned less than we asked for -- avoid blocking
if(r != toRead)
{
+ BOX_TRACE("Read " << r << " bytes from "
+ "stream, returning");
return read;
}
}
else
{
- // Read the header byte to find out how much there is in the next block
+ // Read the header byte to find out how much there is
+ // in the next block
uint8_t header;
if(mrSource.Read(&header, 1, Timeout) == 0)
{
// Didn't get the byte, return now
+ BOX_TRACE("Read 0 bytes of block header, "
+ "returning with " << read << " bytes "
+ "read this time");
return read;
}
@@ -109,6 +120,8 @@ int ProtocolUncertainStream::Read(void *pBuffer, int NBytes, int Timeout)
{
// All done.
mFinished = true;
+ BOX_TRACE("Stream finished, returning with " <<
+ read << " bytes read this time");
return read;
}
else if(header <= Protocol::ProtocolStreamHeader_MaxEncodedSizeValue)
@@ -126,6 +139,10 @@ int ProtocolUncertainStream::Read(void *pBuffer, int NBytes, int Timeout)
// Bad. It used the reserved values.
THROW_EXCEPTION(ServerException, ProtocolUncertainStreamBadBlockHeader)
}
+
+ BOX_TRACE("Read header byte " << (int)header << ", "
+ "next block has " <<
+ mBytesLeftInCurrentBlock << " bytes");
}
}
diff --git a/lib/server/SSLLib.cpp b/lib/server/SSLLib.cpp
index e9c990b9..de7a941b 100644
--- a/lib/server/SSLLib.cpp
+++ b/lib/server/SSLLib.cpp
@@ -14,12 +14,16 @@
#include <openssl/err.h>
#include <openssl/rand.h>
+#ifdef WIN32
+ #include <wincrypt.h>
+#endif
+
#include "SSLLib.h"
#include "ServerException.h"
#include "MemLeakFindOn.h"
-#ifndef NDEBUG
+#ifndef BOX_RELEASE_BUILD
bool SSLLib__TraceErrors = false;
#endif
@@ -35,7 +39,7 @@ void SSLLib::Initialise()
{
if(!::SSL_library_init())
{
- LogError("Initialisation");
+ LogError("initialising OpenSSL");
THROW_EXCEPTION(ServerException, SSLLibraryInitialisationError)
}
@@ -43,7 +47,37 @@ void SSLLib::Initialise()
::SSL_load_error_strings();
// Extra seeding over and above what's already done by the library
-#ifdef HAVE_RANDOM_DEVICE
+#ifdef WIN32
+ HCRYPTPROV provider;
+ if(!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT))
+ {
+ BOX_LOG_WIN_ERROR("Failed to acquire crypto context");
+ BOX_WARNING("No random device -- additional seeding of "
+ "random number generator not performed.");
+ }
+ else
+ {
+ // must free provider
+ BYTE buf[1024];
+
+ if(!CryptGenRandom(provider, sizeof(buf), buf))
+ {
+ BOX_LOG_WIN_ERROR("Failed to get random data");
+ BOX_WARNING("No random device -- additional seeding of "
+ "random number generator not performed.");
+ }
+ else
+ {
+ RAND_seed(buf, sizeof(buf));
+ }
+
+ if(!CryptReleaseContext(provider, 0))
+ {
+ BOX_LOG_WIN_ERROR("Failed to release crypto context");
+ }
+ }
+#elif HAVE_RANDOM_DEVICE
if(::RAND_load_file(RANDOM_DEVICE, 1024) != 1024)
{
THROW_EXCEPTION(ServerException, SSLRandomInitFailed)
@@ -63,14 +97,14 @@ void SSLLib::Initialise()
// Created: 2003/08/06
//
// --------------------------------------------------------------------------
-void SSLLib::LogError(const char *ErrorDuringAction)
+void SSLLib::LogError(const std::string& rErrorDuringAction)
{
unsigned long errcode;
char errname[256]; // SSL docs say at least 120 bytes
while((errcode = ERR_get_error()) != 0)
{
::ERR_error_string_n(errcode, errname, sizeof(errname));
- BOX_ERROR("SSL error during " << ErrorDuringAction << ": " <<
+ BOX_ERROR("SSL error while " << rErrorDuringAction << ": " <<
errname);
}
}
diff --git a/lib/server/SSLLib.h b/lib/server/SSLLib.h
index cdff4f04..ff4aab19 100644
--- a/lib/server/SSLLib.h
+++ b/lib/server/SSLLib.h
@@ -10,7 +10,7 @@
#ifndef SSLLIB__H
#define SSLLIB__H
-#ifndef NDEBUG
+#ifndef BOX_RELEASE_BUILD
extern bool SSLLib__TraceErrors;
#define SET_DEBUG_SSLLIB_TRACE_ERRORS {SSLLib__TraceErrors = true;}
#else
@@ -29,7 +29,7 @@
namespace SSLLib
{
void Initialise();
- void LogError(const char *ErrorDuringAction);
+ void LogError(const std::string& rErrorDuringAction);
};
#endif // SSLLIB__H
diff --git a/lib/server/ServerControl.cpp b/lib/server/ServerControl.cpp
new file mode 100644
index 00000000..c4668b57
--- /dev/null
+++ b/lib/server/ServerControl.cpp
@@ -0,0 +1,227 @@
+#include "Box.h"
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+ #include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+ #include <signal.h>
+#endif
+
+#include "ServerControl.h"
+#include "Test.h"
+
+#ifdef WIN32
+
+#include "WinNamedPipeStream.h"
+#include "IOStreamGetLine.h"
+#include "BoxPortsAndFiles.h"
+
+static std::string sPipeName;
+
+void SetNamedPipeName(const std::string& rPipeName)
+{
+ sPipeName = rPipeName;
+}
+
+bool SendCommands(const std::string& rCmd)
+{
+ WinNamedPipeStream connection;
+
+ try
+ {
+ connection.Connect(sPipeName);
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to connect to daemon control socket");
+ return false;
+ }
+
+ // For receiving data
+ IOStreamGetLine getLine(connection);
+
+ // Wait for the configuration summary
+ std::string configSummary;
+ if(!getLine.GetLine(configSummary))
+ {
+ BOX_ERROR("Failed to receive configuration summary from daemon");
+ return false;
+ }
+
+ // Was the connection rejected by the server?
+ if(getLine.IsEOF())
+ {
+ BOX_ERROR("Server rejected the connection");
+ return false;
+ }
+
+ // Decode it
+ int autoBackup, updateStoreInterval, minimumFileAge, maxUploadWait;
+ if(::sscanf(configSummary.c_str(), "bbackupd: %d %d %d %d",
+ &autoBackup, &updateStoreInterval,
+ &minimumFileAge, &maxUploadWait) != 4)
+ {
+ BOX_ERROR("Config summary didn't decode");
+ return false;
+ }
+
+ std::string cmds;
+ bool expectResponse;
+
+ if (rCmd != "")
+ {
+ cmds = rCmd;
+ cmds += "\nquit\n";
+ expectResponse = true;
+ }
+ else
+ {
+ cmds = "quit\n";
+ expectResponse = false;
+ }
+
+ connection.Write(cmds.c_str(), cmds.size());
+
+ // Read the response
+ std::string line;
+ bool statusOk = !expectResponse;
+
+ while (expectResponse && !getLine.IsEOF() && getLine.GetLine(line))
+ {
+ // Is this an OK or error line?
+ if (line == "ok")
+ {
+ statusOk = true;
+ }
+ else if (line == "error")
+ {
+ BOX_ERROR(rCmd);
+ break;
+ }
+ else
+ {
+ BOX_WARNING("Unexpected response to command '" <<
+ rCmd << "': " << line)
+ }
+ }
+
+ return statusOk;
+}
+
+bool HUPServer(int pid)
+{
+ return SendCommands("reload");
+}
+
+bool KillServerInternal(int pid)
+{
+ HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, false, pid);
+ if (hProcess == NULL)
+ {
+ BOX_ERROR("Failed to open process " << pid << ": " <<
+ GetErrorMessage(GetLastError()));
+ return false;
+ }
+
+ if (!TerminateProcess(hProcess, 1))
+ {
+ BOX_ERROR("Failed to terminate process " << pid << ": " <<
+ GetErrorMessage(GetLastError()));
+ CloseHandle(hProcess);
+ return false;
+ }
+
+ CloseHandle(hProcess);
+ return true;
+}
+
+#else // !WIN32
+
+bool HUPServer(int pid)
+{
+ if(pid == 0) return false;
+ return ::kill(pid, SIGHUP) == 0;
+}
+
+bool KillServerInternal(int pid)
+{
+ if(pid == 0 || pid == -1) return false;
+ bool killed = (::kill(pid, SIGTERM) == 0);
+ if (!killed)
+ {
+ BOX_LOG_SYS_ERROR("Failed to kill process " << pid);
+ }
+ TEST_THAT(killed);
+ return killed;
+}
+
+#endif // WIN32
+
+bool KillServer(int pid, bool WaitForProcess)
+{
+ if (!KillServerInternal(pid))
+ {
+ return false;
+ }
+
+ #ifdef HAVE_WAITPID
+ if (WaitForProcess)
+ {
+ int status, result;
+
+ result = waitpid(pid, &status, 0);
+ if (result != pid)
+ {
+ BOX_LOG_SYS_ERROR("waitpid failed");
+ }
+ TEST_THAT(result == pid);
+
+ TEST_THAT(WIFEXITED(status));
+ if (WIFEXITED(status))
+ {
+ if (WEXITSTATUS(status) != 0)
+ {
+ BOX_WARNING("process exited with code " <<
+ WEXITSTATUS(status));
+ }
+ TEST_THAT(WEXITSTATUS(status) == 0);
+ }
+ }
+ #endif
+
+ for (int i = 0; i < 30; i++)
+ {
+ if (i == 0)
+ {
+ printf("Waiting for server to die (pid %d): ", pid);
+ }
+
+ printf(".");
+ fflush(stdout);
+
+ if (!ServerIsAlive(pid)) break;
+ ::sleep(1);
+ if (!ServerIsAlive(pid)) break;
+ }
+
+ if (!ServerIsAlive(pid))
+ {
+ printf(" done.\n");
+ }
+ else
+ {
+ printf(" failed!\n");
+ }
+
+ fflush(stdout);
+
+ return !ServerIsAlive(pid);
+}
+
diff --git a/lib/server/ServerControl.h b/lib/server/ServerControl.h
index ce5620c2..b2e51864 100644
--- a/lib/server/ServerControl.h
+++ b/lib/server/ServerControl.h
@@ -3,188 +3,16 @@
#include "Test.h"
-#ifdef WIN32
-
-#include "WinNamedPipeStream.h"
-#include "IOStreamGetLine.h"
-#include "BoxPortsAndFiles.h"
-
-static std::string sPipeName;
-
-static void SetNamedPipeName(const std::string& rPipeName)
-{
- sPipeName = rPipeName;
-}
-
-static bool SendCommands(const std::string& rCmd)
-{
- WinNamedPipeStream connection;
-
- try
- {
- connection.Connect(sPipeName);
- }
- catch(...)
- {
- BOX_ERROR("Failed to connect to daemon control socket");
- return false;
- }
-
- // For receiving data
- IOStreamGetLine getLine(connection);
-
- // Wait for the configuration summary
- std::string configSummary;
- if(!getLine.GetLine(configSummary))
- {
- BOX_ERROR("Failed to receive configuration summary from daemon");
- return false;
- }
-
- // Was the connection rejected by the server?
- if(getLine.IsEOF())
- {
- BOX_ERROR("Server rejected the connection");
- return false;
- }
-
- // Decode it
- int autoBackup, updateStoreInterval, minimumFileAge, maxUploadWait;
- if(::sscanf(configSummary.c_str(), "bbackupd: %d %d %d %d",
- &autoBackup, &updateStoreInterval,
- &minimumFileAge, &maxUploadWait) != 4)
- {
- BOX_ERROR("Config summary didn't decode");
- return false;
- }
-
- std::string cmds;
- bool expectResponse;
-
- if (rCmd != "")
- {
- cmds = rCmd;
- cmds += "\nquit\n";
- expectResponse = true;
- }
- else
- {
- cmds = "quit\n";
- expectResponse = false;
- }
-
- connection.Write(cmds.c_str(), cmds.size());
-
- // Read the response
- std::string line;
- bool statusOk = !expectResponse;
-
- while (expectResponse && !getLine.IsEOF() && getLine.GetLine(line))
- {
- // Is this an OK or error line?
- if (line == "ok")
- {
- statusOk = true;
- }
- else if (line == "error")
- {
- BOX_ERROR(rCmd);
- break;
- }
- else
- {
- BOX_WARNING("Unexpected response to command '" <<
- rCmd << "': " << line)
- }
- }
-
- return statusOk;
-}
-
-inline bool HUPServer(int pid)
-{
- return SendCommands("reload");
-}
+bool HUPServer(int pid);
+bool KillServer(int pid, bool WaitForProcess = false);
-inline bool KillServerInternal(int pid)
-{
- HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, false, pid);
- if (hProcess == NULL)
- {
- BOX_ERROR("Failed to open process " << pid << ": " <<
- GetErrorMessage(GetLastError()));
- return false;
- }
-
- if (!TerminateProcess(hProcess, 1))
- {
- BOX_ERROR("Failed to terminate process " << pid << ": " <<
- GetErrorMessage(GetLastError()));
- CloseHandle(hProcess);
- return false;
- }
-
- CloseHandle(hProcess);
- return true;
-}
-
-#else // !WIN32
-
-inline bool HUPServer(int pid)
-{
- if(pid == 0) return false;
- return ::kill(pid, SIGHUP) == 0;
-}
-
-inline bool KillServerInternal(int pid)
-{
- if(pid == 0 || pid == -1) return false;
- bool killed = (::kill(pid, SIGTERM) == 0);
- if (!killed)
- {
- BOX_ERROR("Failed to kill process " << pid << ": " <<
- strerror(errno));
- }
- TEST_THAT(killed);
- return killed;
-}
+#ifdef WIN32
+ #include "WinNamedPipeStream.h"
+ #include "IOStreamGetLine.h"
+ #include "BoxPortsAndFiles.h"
+ void SetNamedPipeName(const std::string& rPipeName);
+ // bool SendCommands(const std::string& rCmd);
#endif // WIN32
-inline bool KillServer(int pid)
-{
- if (!KillServerInternal(pid))
- {
- return false;
- }
-
- for (int i = 0; i < 30; i++)
- {
- if (i == 0)
- {
- printf("Waiting for server to die: ");
- }
-
- printf(".");
- fflush(stdout);
-
- if (!ServerIsAlive(pid)) break;
- ::sleep(1);
- if (!ServerIsAlive(pid)) break;
- }
-
- if (!ServerIsAlive(pid))
- {
- printf(" done.\n");
- }
- else
- {
- printf(" failed!\n");
- }
-
- fflush(stdout);
-
- return !ServerIsAlive(pid);
-}
-
#endif // SERVER_CONTROL_H
diff --git a/lib/server/ServerStream.h b/lib/server/ServerStream.h
index 32a57bac..34de7def 100644
--- a/lib/server/ServerStream.h
+++ b/lib/server/ServerStream.h
@@ -108,7 +108,7 @@ public:
{
// Child task, dump leaks to trace, which we make sure is on
#ifdef BOX_MEMORY_LEAK_TESTING
- #ifndef NDEBUG
+ #ifndef BOX_RELEASE_BUILD
TRACE_TO_SYSLOG(true);
TRACE_TO_STDOUT(true);
#endif
@@ -120,13 +120,19 @@ public:
}
}
+protected:
+ virtual void NotifyListenerIsReady() { }
+
+public:
virtual void Run2(bool &rChildExit)
{
try
{
- // Wait object with a timeout of 10 seconds, which is a reasonable time to wait before
- // cleaning up finished child processes.
- WaitForEvent connectionWait(10000);
+ // Wait object with a timeout of 1 second, which
+ // is a reasonable time to wait before cleaning up
+ // finished child processes, and allows the daemon
+ // to terminate reasonably quickly on request.
+ WaitForEvent connectionWait(1000);
// BLOCK
{
@@ -218,6 +224,8 @@ public:
}
}
}
+
+ NotifyListenerIsReady();
while(!StopRun())
{
@@ -273,7 +281,7 @@ public:
}
// Log it
- BOX_WARNING("Message from child process " << pid << ": " << logMessage);
+ BOX_NOTICE("Message from child process " << pid << ": " << logMessage);
}
else
{
@@ -365,8 +373,8 @@ private:
};
#define SERVERSTREAM_VERIFY_SERVER_KEYS(DEFAULT_ADDRESSES) \
- {"ListenAddresses", DEFAULT_ADDRESSES, 0, 0}, \
- DAEMON_VERIFY_SERVER_KEYS
+ ConfigurationVerifyKey("ListenAddresses", 0, DEFAULT_ADDRESSES), \
+ DAEMON_VERIFY_SERVER_KEYS
#include "MemLeakFindOff.h"
diff --git a/lib/server/ServerTLS.h b/lib/server/ServerTLS.h
index 71d35380..a74a671e 100644
--- a/lib/server/ServerTLS.h
+++ b/lib/server/ServerTLS.h
@@ -55,7 +55,8 @@ public:
mContext.Initialise(true /* as server */, certFile.c_str(), keyFile.c_str(), caFile.c_str());
// Then do normal stream server stuff
- ServerStream<SocketStreamTLS, Port, ListenBacklog>::Run2(rChildExit);
+ ServerStream<SocketStreamTLS, Port, ListenBacklog,
+ ForkToHandleRequests>::Run2(rChildExit);
}
virtual void HandleConnection(SocketStreamTLS &rStream)
@@ -70,11 +71,10 @@ private:
};
#define SERVERTLS_VERIFY_SERVER_KEYS(DEFAULT_ADDRESSES) \
- {"CertificateFile", 0, ConfigTest_Exists, 0}, \
- {"PrivateKeyFile", 0, ConfigTest_Exists, 0}, \
- {"TrustedCAsFile", 0, ConfigTest_Exists, 0}, \
- SERVERSTREAM_VERIFY_SERVER_KEYS(DEFAULT_ADDRESSES)
-
+ ConfigurationVerifyKey("CertificateFile", ConfigTest_Exists), \
+ ConfigurationVerifyKey("PrivateKeyFile", ConfigTest_Exists), \
+ ConfigurationVerifyKey("TrustedCAsFile", ConfigTest_Exists), \
+ SERVERSTREAM_VERIFY_SERVER_KEYS(DEFAULT_ADDRESSES)
#endif // SERVERTLS__H
diff --git a/lib/server/Socket.cpp b/lib/server/Socket.cpp
index 28dae69f..4a83bdb0 100644
--- a/lib/server/Socket.cpp
+++ b/lib/server/Socket.cpp
@@ -32,12 +32,15 @@
// --------------------------------------------------------------------------
//
// Function
-// Name: Socket::NameLookupToSockAddr(SocketAllAddr &, int, char *, int)
+// Name: Socket::NameLookupToSockAddr(SocketAllAddr &, int,
+// char *, int)
// Purpose: Sets up a sockaddr structure given a name and type
// Created: 2003/07/31
//
// --------------------------------------------------------------------------
-void Socket::NameLookupToSockAddr(SocketAllAddr &addr, int &sockDomain, int Type, const char *Name, int Port, int &rSockAddrLenOut)
+void Socket::NameLookupToSockAddr(SocketAllAddr &addr, int &sockDomain,
+ enum Type Type, const std::string& rName, int Port,
+ int &rSockAddrLenOut)
{
int sockAddrLen = 0;
@@ -47,7 +50,7 @@ void Socket::NameLookupToSockAddr(SocketAllAddr &addr, int &sockDomain, int Type
sockDomain = AF_INET;
{
// Lookup hostname
- struct hostent *phost = ::gethostbyname(Name);
+ struct hostent *phost = ::gethostbyname(rName.c_str());
if(phost != NULL)
{
if(phost->h_addr_list[0] != 0)
@@ -81,7 +84,7 @@ void Socket::NameLookupToSockAddr(SocketAllAddr &addr, int &sockDomain, int Type
sockDomain = AF_UNIX;
{
// Check length of name is OK
- unsigned int nameLen = ::strlen(Name);
+ unsigned int nameLen = rName.length();
if(nameLen >= (sizeof(addr.sa_unix.sun_path) - 1))
{
THROW_EXCEPTION(ServerException, SocketNameUNIXPathTooLong);
@@ -91,7 +94,9 @@ void Socket::NameLookupToSockAddr(SocketAllAddr &addr, int &sockDomain, int Type
addr.sa_unix.sun_len = sockAddrLen;
#endif
addr.sa_unix.sun_family = PF_UNIX;
- ::strcpy(addr.sa_unix.sun_path, Name);
+ ::strncpy(addr.sa_unix.sun_path, rName.c_str(),
+ sizeof(addr.sa_unix.sun_path) - 1);
+ addr.sa_unix.sun_path[sizeof(addr.sa_unix.sun_path)-1] = 0;
}
break;
#endif
diff --git a/lib/server/Socket.h b/lib/server/Socket.h
index 057e4cad..5034dbd8 100644
--- a/lib/server/Socket.h
+++ b/lib/server/Socket.h
@@ -39,13 +39,15 @@ typedef union {
// --------------------------------------------------------------------------
namespace Socket
{
- enum
+ enum Type
{
TypeINET = 1,
TypeUNIX = 2
};
- void NameLookupToSockAddr(SocketAllAddr &addr, int &sockDomain, int Type, const char *Name, int Port, int &rSockAddrLenOut);
+ void NameLookupToSockAddr(SocketAllAddr &addr, int &sockDomain,
+ enum Type type, const std::string& rName, int Port,
+ int &rSockAddrLenOut);
void LogIncomingConnection(const struct sockaddr *addr, socklen_t addrlen);
std::string IncomingConnectionLogMessage(const struct sockaddr *addr, socklen_t addrlen);
};
diff --git a/lib/server/SocketListen.h b/lib/server/SocketListen.h
index ff08fb8f..586adf22 100644
--- a/lib/server/SocketListen.h
+++ b/lib/server/SocketListen.h
@@ -108,45 +108,57 @@ public:
if(::close(mSocketHandle) == -1)
#endif
{
- THROW_EXCEPTION(ServerException, SocketCloseError)
+ BOX_LOG_SYS_ERROR("Failed to close network "
+ "socket");
+ THROW_EXCEPTION(ServerException,
+ SocketCloseError)
}
}
mSocketHandle = -1;
}
- // --------------------------------------------------------------------------
+ // ------------------------------------------------------------------
//
// Function
// Name: SocketListen::Listen(int, char*, int)
// Purpose: Initialises, starts the socket listening.
// Created: 2003/07/31
//
- // --------------------------------------------------------------------------
- void Listen(int Type, const char *Name, int Port = 0)
+ // ------------------------------------------------------------------
+ void Listen(Socket::Type Type, const char *Name, int Port = 0)
{
- if(mSocketHandle != -1) {THROW_EXCEPTION(ServerException, SocketAlreadyOpen)}
+ if(mSocketHandle != -1)
+ {
+ THROW_EXCEPTION(ServerException, SocketAlreadyOpen);
+ }
// Setup parameters based on type, looking up names if required
int sockDomain = 0;
SocketAllAddr addr;
int addrLen = 0;
- Socket::NameLookupToSockAddr(addr, sockDomain, Type, Name, Port, addrLen);
+ Socket::NameLookupToSockAddr(addr, sockDomain, Type, Name,
+ Port, addrLen);
// Create the socket
- mSocketHandle = ::socket(sockDomain, SOCK_STREAM, 0 /* let OS choose protocol */);
+ mSocketHandle = ::socket(sockDomain, SOCK_STREAM,
+ 0 /* let OS choose protocol */);
if(mSocketHandle == -1)
{
+ BOX_LOG_SYS_ERROR("Failed to create a network socket");
THROW_EXCEPTION(ServerException, SocketOpenError)
}
// Set an option to allow reuse (useful for -HUP situations!)
#ifdef WIN32
- if(::setsockopt(mSocketHandle, SOL_SOCKET, SO_REUSEADDR, "", 0) == -1)
+ if(::setsockopt(mSocketHandle, SOL_SOCKET, SO_REUSEADDR, "",
+ 0) == -1)
#else
int option = true;
- if(::setsockopt(mSocketHandle, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) == -1)
+ if(::setsockopt(mSocketHandle, SOL_SOCKET, SO_REUSEADDR,
+ &option, sizeof(option)) == -1)
#endif
{
+ BOX_LOG_SYS_ERROR("Failed to set socket options");
THROW_EXCEPTION(ServerException, SocketOpenError)
}
@@ -161,19 +173,25 @@ public:
}
}
- // --------------------------------------------------------------------------
+ // ------------------------------------------------------------------
//
// Function
// Name: SocketListen::Accept(int)
- // Purpose: Accepts a connection, returning a pointer to a class of
- // the specified type. May return a null pointer if a signal happens,
- // or there's a timeout. Timeout specified in milliseconds, defaults to infinite time.
+ // Purpose: Accepts a connection, returning a pointer to
+ // a class of the specified type. May return a
+ // null pointer if a signal happens, or there's
+ // a timeout. Timeout specified in
+ // milliseconds, defaults to infinite time.
// Created: 2003/07/31
//
- // --------------------------------------------------------------------------
- std::auto_ptr<SocketType> Accept(int Timeout = INFTIM, std::string *pLogMsg = 0)
+ // ------------------------------------------------------------------
+ std::auto_ptr<SocketType> Accept(int Timeout = INFTIM,
+ std::string *pLogMsg = 0)
{
- if(mSocketHandle == -1) {THROW_EXCEPTION(ServerException, BadSocketHandle)}
+ if(mSocketHandle == -1)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle);
+ }
// Do the accept, using the supplied locking type
int sock;
@@ -185,8 +203,10 @@ public:
if(!socklock.HaveLock())
{
- // Didn't get the lock for some reason. Wait a while, then
- // return nothing.
+ // Didn't get the lock for some reason.
+ // Wait a while, then return nothing.
+ BOX_ERROR("Failed to get a lock on incoming "
+ "connection");
::sleep(1);
return std::auto_ptr<SocketType>();
}
@@ -202,12 +222,18 @@ public:
// signal?
if(errno == EINTR)
{
+ BOX_ERROR("Failed to accept "
+ "connection: interrupted by "
+ "signal");
// return nothing
return std::auto_ptr<SocketType>();
}
else
{
- THROW_EXCEPTION(ServerException, SocketPollError)
+ BOX_LOG_SYS_ERROR("Failed to poll "
+ "connection");
+ THROW_EXCEPTION(ServerException,
+ SocketPollError)
}
break;
case 0: // timed out
@@ -220,16 +246,19 @@ public:
sock = ::accept(mSocketHandle, &addr, &addrlen);
}
- // Got socket (or error), unlock (implcit in destruction)
+
+ // Got socket (or error), unlock (implicit in destruction)
if(sock == -1)
{
+ BOX_LOG_SYS_ERROR("Failed to accept connection");
THROW_EXCEPTION(ServerException, SocketAcceptError)
}
// Log it
if(pLogMsg)
{
- *pLogMsg = Socket::IncomingConnectionLogMessage(&addr, addrlen);
+ *pLogMsg = Socket::IncomingConnectionLogMessage(&addr,
+ addrlen);
}
else
{
@@ -243,27 +272,29 @@ public:
// Functions to allow adding to WaitForEvent class, for efficient waiting
// on multiple sockets.
#ifdef HAVE_KQUEUE
- // --------------------------------------------------------------------------
+ // ------------------------------------------------------------------
//
// Function
// Name: SocketListen::FillInKEevent
// Purpose: Fills in a kevent structure for this socket
// Created: 9/3/04
//
- // --------------------------------------------------------------------------
+ // ------------------------------------------------------------------
void FillInKEvent(struct kevent &rEvent, int Flags = 0) const
{
- EV_SET(&rEvent, mSocketHandle, EVFILT_READ, 0, 0, 0, (void*)this);
+ EV_SET(&rEvent, mSocketHandle, EVFILT_READ, 0, 0, 0,
+ (void*)this);
}
#else
- // --------------------------------------------------------------------------
+ // ------------------------------------------------------------------
//
// Function
// Name: SocketListen::FillInPoll
- // Purpose: Fills in the data necessary for a poll operation
+ // Purpose: Fills in the data necessary for a poll
+ // operation
// Created: 9/3/04
//
- // --------------------------------------------------------------------------
+ // ------------------------------------------------------------------
void FillInPoll(int &fd, short &events, int Flags = 0) const
{
fd = mSocketHandle;
diff --git a/lib/server/SocketStream.cpp b/lib/server/SocketStream.cpp
index 5cb252bd..95b4b4f4 100644
--- a/lib/server/SocketStream.cpp
+++ b/lib/server/SocketStream.cpp
@@ -18,7 +18,11 @@
#include <string.h>
#ifndef WIN32
-#include <poll.h>
+ #include <poll.h>
+#endif
+
+#ifdef HAVE_UCRED_H
+ #include <ucred.h>
#endif
#include "SocketStream.h"
@@ -123,20 +127,23 @@ void SocketStream::Attach(int socket)
THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
}
- mSocketHandle = socket;
ResetCounters();
+
+ mSocketHandle = socket;
+ mReadClosed = false;
+ mWriteClosed = false;
}
// --------------------------------------------------------------------------
//
// Function
-// Name: SocketStream::Open(int, char *, int)
+// Name: SocketStream::Open(Socket::Type, char *, int)
// Purpose: Opens a connection to a listening socket (INET or UNIX)
// Created: 2003/07/31
//
// --------------------------------------------------------------------------
-void SocketStream::Open(int Type, const char *Name, int Port)
+void SocketStream::Open(Socket::Type Type, const std::string& rName, int Port)
{
if(mSocketHandle != INVALID_SOCKET_VALUE)
{
@@ -147,12 +154,14 @@ void SocketStream::Open(int Type, const char *Name, int Port)
int sockDomain = 0;
SocketAllAddr addr;
int addrLen = 0;
- Socket::NameLookupToSockAddr(addr, sockDomain, Type, Name, Port, addrLen);
+ Socket::NameLookupToSockAddr(addr, sockDomain, Type, rName, Port, addrLen);
// Create the socket
- mSocketHandle = ::socket(sockDomain, SOCK_STREAM, 0 /* let OS choose protocol */);
+ mSocketHandle = ::socket(sockDomain, SOCK_STREAM,
+ 0 /* let OS choose protocol */);
if(mSocketHandle == INVALID_SOCKET_VALUE)
{
+ BOX_LOG_SYS_ERROR("Failed to create a network socket");
THROW_EXCEPTION(ServerException, SocketOpenError)
}
@@ -163,28 +172,24 @@ void SocketStream::Open(int Type, const char *Name, int Port)
#ifdef WIN32
DWORD err = WSAGetLastError();
::closesocket(mSocketHandle);
-#else
- int err = errno;
+ BOX_LOG_WIN_ERROR_NUMBER("Failed to connect to socket "
+ "(type " << Type << ", name " << rName <<
+ ", port " << Port << ")", err);
+#else // !WIN32
+ BOX_LOG_SYS_ERROR("Failed to connect to socket (type " <<
+ Type << ", name " << rName << ", port " << Port <<
+ ")");
::close(mSocketHandle);
-#endif
-
-#ifdef WIN32
- BOX_ERROR("Failed to connect to socket (type " << Type <<
- ", name " << Name << ", port " << Port << "): " <<
- GetErrorMessage(err)
- );
-#else
- BOX_ERROR("Failed to connect to socket (type " << Type <<
- ", name " << Name << ", port " << Port << "): " <<
- strerror(err) << " (" << err << ")"
- );
-#endif
+#endif // WIN32
mSocketHandle = INVALID_SOCKET_VALUE;
THROW_EXCEPTION(ConnectionException, Conn_SocketConnectError)
}
ResetCounters();
+
+ mReadClosed = false;
+ mWriteClosed = false;
}
// --------------------------------------------------------------------------
@@ -220,7 +225,9 @@ int SocketStream::Read(void *pBuffer, int NBytes, int Timeout)
else
{
// Bad!
- THROW_EXCEPTION(ServerException, SocketPollError)
+ BOX_LOG_SYS_ERROR("Failed to poll socket");
+ THROW_EXCEPTION(ServerException,
+ SocketPollError)
}
break;
@@ -250,9 +257,12 @@ int SocketStream::Read(void *pBuffer, int NBytes, int Timeout)
else
{
// Other error
- THROW_EXCEPTION(ConnectionException, Conn_SocketReadError)
+ BOX_LOG_SYS_ERROR("Failed to read from socket");
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketReadError);
}
}
+
// Closed for reading?
if(r == 0)
{
@@ -297,7 +307,9 @@ void SocketStream::Write(const void *pBuffer, int NBytes)
{
// Error.
mWriteClosed = true; // assume can't write again
- THROW_EXCEPTION(ConnectionException, Conn_SocketWriteError)
+ BOX_LOG_SYS_ERROR("Failed to write to socket");
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketWriteError);
}
// Knock off bytes sent
@@ -310,7 +322,9 @@ void SocketStream::Write(const void *pBuffer, int NBytes)
// Need to wait until it can send again?
if(bytesLeft > 0)
{
- TRACE3("Waiting to send data on socket %d, (%d to send of %d)\n", mSocketHandle, bytesLeft, NBytes);
+ BOX_TRACE("Waiting to send data on socket " <<
+ mSocketHandle << " (" << bytesLeft <<
+ " of " << NBytes << " bytes left)");
// Wait for data to send.
struct pollfd p;
@@ -323,7 +337,10 @@ void SocketStream::Write(const void *pBuffer, int NBytes)
// Don't exception if it's just a signal
if(errno != EINTR)
{
- THROW_EXCEPTION(ServerException, SocketPollError)
+ BOX_LOG_SYS_ERROR("Failed to poll "
+ "socket");
+ THROW_EXCEPTION(ServerException,
+ SocketPollError)
}
}
}
@@ -350,7 +367,9 @@ void SocketStream::Close()
if(::close(mSocketHandle) == -1)
#endif
{
- THROW_EXCEPTION(ServerException, SocketCloseError)
+ BOX_LOG_SYS_ERROR("Failed to close socket");
+ // don't throw an exception here, assume that the socket was
+ // already closed or closing.
}
mSocketHandle = INVALID_SOCKET_VALUE;
}
@@ -380,6 +399,7 @@ void SocketStream::Shutdown(bool Read, bool Write)
// Shut it down!
if(::shutdown(mSocketHandle, how) == -1)
{
+ BOX_LOG_SYS_ERROR("Failed to shutdown socket");
THROW_EXCEPTION(ConnectionException, Conn_SocketShutdownError)
}
}
@@ -458,18 +478,37 @@ bool SocketStream::GetPeerCredentials(uid_t &rUidOut, gid_t &rGidOut)
struct ucred cred;
socklen_t credLen = sizeof(cred);
- if(::getsockopt(mSocketHandle, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == 0)
+ if(::getsockopt(mSocketHandle, SOL_SOCKET, SO_PEERCRED, &cred,
+ &credLen) == 0)
{
rUidOut = cred.uid;
rGidOut = cred.gid;
return true;
}
+
+ BOX_LOG_SYS_ERROR("Failed to get peer credentials on socket");
+#endif
+
+#if defined HAVE_UCRED_H && HAVE_GETPEERUCRED
+ ucred_t *pucred = NULL;
+ if(::getpeerucred(mSocketHandle, &pucred) == 0)
+ {
+ rUidOut = ucred_geteuid(pucred);
+ rGidOut = ucred_getegid(pucred);
+ ucred_free(pucred);
+ if (rUidOut == -1 || rGidOut == -1)
+ {
+ BOX_ERROR("Failed to get peer credentials on "
+ "socket: insufficient information");
+ return false;
+ }
+ return true;
+ }
+
+ BOX_LOG_SYS_ERROR("Failed to get peer credentials on socket");
#endif
// Not available
return false;
}
-
-
-
diff --git a/lib/server/SocketStream.h b/lib/server/SocketStream.h
index 51f2e306..2b582f21 100644
--- a/lib/server/SocketStream.h
+++ b/lib/server/SocketStream.h
@@ -11,6 +11,7 @@
#define SOCKETSTREAM__H
#include "IOStream.h"
+#include "Socket.h"
#ifdef WIN32
typedef SOCKET tOSSocketHandle;
@@ -36,7 +37,7 @@ public:
SocketStream(const SocketStream &rToCopy);
~SocketStream();
- void Open(int Type, const char *Name, int Port = 0);
+ void Open(Socket::Type Type, const std::string& rName, int Port = 0);
void Attach(int socket);
virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
diff --git a/lib/server/SocketStreamTLS.cpp b/lib/server/SocketStreamTLS.cpp
index 58dc5754..19fdadd4 100644
--- a/lib/server/SocketStreamTLS.cpp
+++ b/lib/server/SocketStreamTLS.cpp
@@ -99,9 +99,10 @@ SocketStreamTLS::~SocketStreamTLS()
// Created: 2003/08/06
//
// --------------------------------------------------------------------------
-void SocketStreamTLS::Open(const TLSContext &rContext, int Type, const char *Name, int Port)
+void SocketStreamTLS::Open(const TLSContext &rContext, Socket::Type Type,
+ const std::string& rName, int Port)
{
- SocketStream::Open(Type, Name, Port);
+ SocketStream::Open(Type, rName, Port);
Handshake(rContext);
ResetCounters();
}
@@ -123,7 +124,7 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer)
mpBIO = ::BIO_new(::BIO_s_socket());
if(mpBIO == 0)
{
- SSLLib::LogError("Create socket bio");
+ SSLLib::LogError("creating socket bio");
THROW_EXCEPTION(ServerException, TLSAllocationFailed)
}
@@ -134,7 +135,7 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer)
mpSSL = ::SSL_new(rContext.GetRawContext());
if(mpSSL == 0)
{
- SSLLib::LogError("Create ssl");
+ SSLLib::LogError("creating SSL object");
THROW_EXCEPTION(ServerException, TLSAllocationFailed)
}
@@ -202,12 +203,12 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer)
// Error occured
if(IsServer)
{
- SSLLib::LogError("Accept");
+ SSLLib::LogError("accepting connection");
THROW_EXCEPTION(ConnectionException, Conn_TLSHandshakeFailed)
}
else
{
- SSLLib::LogError("Connect");
+ SSLLib::LogError("connecting");
THROW_EXCEPTION(ConnectionException, Conn_TLSHandshakeFailed)
}
}
@@ -334,7 +335,7 @@ int SocketStreamTLS::Read(void *pBuffer, int NBytes, int Timeout)
break;
default:
- SSLLib::LogError("Read");
+ SSLLib::LogError("reading");
THROW_EXCEPTION(ConnectionException, Conn_TLSReadFailed)
break;
}
@@ -390,7 +391,7 @@ void SocketStreamTLS::Write(const void *pBuffer, int NBytes)
case SSL_ERROR_WANT_WRITE:
// wait for the requried data
{
- #ifndef NDEBUG
+ #ifndef BOX_RELEASE_BUILD
bool conditionmet =
#endif
WaitWhenRetryRequired(se, IOStream::TimeOutInfinite);
@@ -399,7 +400,7 @@ void SocketStreamTLS::Write(const void *pBuffer, int NBytes)
break;
default:
- SSLLib::LogError("Write");
+ SSLLib::LogError("writing");
THROW_EXCEPTION(ConnectionException, Conn_TLSWriteFailed)
break;
}
@@ -441,7 +442,7 @@ void SocketStreamTLS::Shutdown(bool Read, bool Write)
if(::SSL_shutdown(mpSSL) < 0)
{
- SSLLib::LogError("Shutdown");
+ SSLLib::LogError("shutting down");
THROW_EXCEPTION(ConnectionException, Conn_TLSShutdownFailed)
}
diff --git a/lib/server/SocketStreamTLS.h b/lib/server/SocketStreamTLS.h
index 64e52833..bb40ed10 100644
--- a/lib/server/SocketStreamTLS.h
+++ b/lib/server/SocketStreamTLS.h
@@ -38,7 +38,8 @@ private:
SocketStreamTLS(const SocketStreamTLS &rToCopy);
public:
- void Open(const TLSContext &rContext, int Type, const char *Name, int Port = 0);
+ void Open(const TLSContext &rContext, Socket::Type Type,
+ const std::string& rName, int Port = 0);
void Handshake(const TLSContext &rContext, bool IsServer = false);
virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
diff --git a/lib/server/TLSContext.cpp b/lib/server/TLSContext.cpp
index 49143801..ebc7384a 100644
--- a/lib/server/TLSContext.cpp
+++ b/lib/server/TLSContext.cpp
@@ -75,19 +75,25 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c
// Setup our identity
if(::SSL_CTX_use_certificate_chain_file(mpContext, CertificatesFile) != 1)
{
- SSLLib::LogError("Load certificates");
+ std::string msg = "loading certificates from ";
+ msg += CertificatesFile;
+ SSLLib::LogError(msg);
THROW_EXCEPTION(ServerException, TLSLoadCertificatesFailed)
}
if(::SSL_CTX_use_PrivateKey_file(mpContext, PrivateKeyFile, SSL_FILETYPE_PEM) != 1)
{
- SSLLib::LogError("Load private key");
+ std::string msg = "loading private key from ";
+ msg += PrivateKeyFile;
+ SSLLib::LogError(msg);
THROW_EXCEPTION(ServerException, TLSLoadPrivateKeyFailed)
}
// Setup the identify of CAs we trust
if(::SSL_CTX_load_verify_locations(mpContext, TrustedCAsFile, NULL) != 1)
{
- SSLLib::LogError("Load CA cert");
+ std::string msg = "loading CA cert from ";
+ msg += TrustedCAsFile;
+ SSLLib::LogError(msg);
THROW_EXCEPTION(ServerException, TLSLoadTrustedCAsFailed)
}
@@ -99,7 +105,7 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c
// Setup allowed ciphers
if(::SSL_CTX_set_cipher_list(mpContext, CIPHER_LIST) != 1)
{
- SSLLib::LogError("Set cipher list");
+ SSLLib::LogError("setting cipher list to " CIPHER_LIST);
THROW_EXCEPTION(ServerException, TLSSetCiphersFailed)
}
}
diff --git a/lib/server/WinNamedPipeListener.h b/lib/server/WinNamedPipeListener.h
new file mode 100644
index 00000000..26e76e3d
--- /dev/null
+++ b/lib/server/WinNamedPipeListener.h
@@ -0,0 +1,232 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: WinNamedPipeListener.h
+// Purpose: Windows named pipe socket connection listener
+// for server
+// Created: 2008/09/30
+//
+// --------------------------------------------------------------------------
+
+#ifndef WINNAMEDPIPELISTENER__H
+#define WINNAMEDPIPELISTENER__H
+
+#include <OverlappedIO.h>
+#include <WinNamedPipeStream.h>
+
+#include "ServerException.h"
+
+#include "MemLeakFindOn.h"
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: WinNamedPipeListener
+// Purpose:
+// Created: 2008/09/30
+//
+// --------------------------------------------------------------------------
+template<int ListenBacklog = 128>
+class WinNamedPipeListener
+{
+private:
+ std::auto_ptr<std::string> mapPipeName;
+ std::auto_ptr<OverlappedIO> mapOverlapConnect;
+ HANDLE mPipeHandle;
+
+public:
+ // Initialise
+ WinNamedPipeListener()
+ : mPipeHandle(INVALID_HANDLE_VALUE)
+ { }
+
+private:
+ WinNamedPipeListener(const WinNamedPipeListener &rToCopy)
+ { /* forbidden */ }
+
+ HANDLE CreatePipeHandle(const std::string& rName)
+ {
+ std::string socket = WinNamedPipeStream::sPipeNamePrefix +
+ rName;
+
+ HANDLE handle = CreateNamedPipeA(
+ socket.c_str(), // pipe name
+ PIPE_ACCESS_DUPLEX | // read/write access
+ FILE_FLAG_OVERLAPPED, // enabled overlapped I/O
+ PIPE_TYPE_BYTE | // message type pipe
+ PIPE_READMODE_BYTE | // message-read mode
+ PIPE_WAIT, // blocking mode
+ ListenBacklog + 1, // max. instances
+ 4096, // output buffer size
+ 4096, // input buffer size
+ NMPWAIT_USE_DEFAULT_WAIT, // client time-out
+ NULL); // default security attribute
+
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ BOX_LOG_WIN_ERROR("Failed to create named pipe " <<
+ socket);
+ THROW_EXCEPTION(ServerException, SocketOpenError)
+ }
+
+ return handle;
+ }
+
+public:
+ ~WinNamedPipeListener()
+ {
+ Close();
+ }
+
+ void Close()
+ {
+ if (mPipeHandle != INVALID_HANDLE_VALUE)
+ {
+ if (mapOverlapConnect.get())
+ {
+ // outstanding connect in progress
+ if (CancelIo(mPipeHandle) != TRUE)
+ {
+ BOX_LOG_WIN_ERROR("Failed to cancel "
+ "outstanding connect request "
+ "on named pipe");
+ }
+
+ mapOverlapConnect.reset();
+ }
+
+ if (CloseHandle(mPipeHandle) != TRUE)
+ {
+ BOX_LOG_WIN_ERROR("Failed to close named pipe "
+ "handle");
+ }
+
+ mPipeHandle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ // ------------------------------------------------------------------
+ //
+ // Function
+ // Name: WinNamedPipeListener::Listen(std::string name)
+ // Purpose: Initialises socket name
+ // Created: 2003/07/31
+ //
+ // ------------------------------------------------------------------
+ void Listen(const std::string& rName)
+ {
+ Close();
+ mapPipeName.reset(new std::string(rName));
+ mPipeHandle = CreatePipeHandle(rName);
+ }
+
+ // ------------------------------------------------------------------
+ //
+ // Function
+ // Name: WinNamedPipeListener::Accept(int)
+ // Purpose: Accepts a connection, returning a pointer to
+ // a class of the specified type. May return a
+ // null pointer if a signal happens, or there's
+ // a timeout. Timeout specified in
+ // milliseconds, defaults to infinite time.
+ // Created: 2003/07/31
+ //
+ // ------------------------------------------------------------------
+ std::auto_ptr<WinNamedPipeStream> Accept(int Timeout = INFTIM,
+ const char* pLogMsgOut = NULL)
+ {
+ if(!mapPipeName.get())
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle);
+ }
+
+ BOOL connected = FALSE;
+ std::auto_ptr<WinNamedPipeStream> mapStream;
+
+ if (!mapOverlapConnect.get())
+ {
+ // start a new connect operation
+ mapOverlapConnect.reset(new OverlappedIO());
+ connected = ConnectNamedPipe(mPipeHandle,
+ &mapOverlapConnect->mOverlapped);
+
+ if (connected == FALSE)
+ {
+ if (GetLastError() == ERROR_PIPE_CONNECTED)
+ {
+ connected = TRUE;
+ }
+ else if (GetLastError() != ERROR_IO_PENDING)
+ {
+ BOX_LOG_WIN_ERROR("Failed to connect "
+ "named pipe");
+ THROW_EXCEPTION(ServerException,
+ SocketAcceptError);
+ }
+ }
+ }
+
+ if (connected == FALSE)
+ {
+ // wait for connection
+ DWORD result = WaitForSingleObject(
+ mapOverlapConnect->mOverlapped.hEvent,
+ (Timeout == INFTIM) ? INFINITE : Timeout);
+
+ if (result == WAIT_OBJECT_0)
+ {
+ DWORD dummy;
+
+ if (!GetOverlappedResult(mPipeHandle,
+ &mapOverlapConnect->mOverlapped,
+ &dummy, TRUE))
+ {
+ BOX_LOG_WIN_ERROR("Failed to get "
+ "overlapped connect result");
+ THROW_EXCEPTION(ServerException,
+ SocketAcceptError);
+ }
+
+ connected = TRUE;
+ }
+ else if (result == WAIT_TIMEOUT)
+ {
+ return mapStream; // contains NULL
+ }
+ else if (result == WAIT_ABANDONED)
+ {
+ BOX_ERROR("Wait for named pipe connection "
+ "was abandoned by the system");
+ THROW_EXCEPTION(ServerException,
+ SocketAcceptError);
+ }
+ else if (result == WAIT_FAILED)
+ {
+ BOX_LOG_WIN_ERROR("Failed to wait for named "
+ "pipe connection");
+ THROW_EXCEPTION(ServerException,
+ SocketAcceptError);
+ }
+ else
+ {
+ BOX_ERROR("Failed to wait for named pipe "
+ "connection: unknown return code " <<
+ result);
+ THROW_EXCEPTION(ServerException,
+ SocketAcceptError);
+ }
+ }
+
+ ASSERT(connected == TRUE);
+
+ mapStream.reset(new WinNamedPipeStream(mPipeHandle));
+ mPipeHandle = CreatePipeHandle(*mapPipeName);
+ mapOverlapConnect.reset();
+
+ return mapStream;
+ }
+};
+
+#include "MemLeakFindOff.h"
+
+#endif // WINNAMEDPIPELISTENER__H
diff --git a/lib/server/WinNamedPipeStream.cpp b/lib/server/WinNamedPipeStream.cpp
index fedb57d8..1179516e 100644
--- a/lib/server/WinNamedPipeStream.cpp
+++ b/lib/server/WinNamedPipeStream.cpp
@@ -44,7 +44,55 @@ WinNamedPipeStream::WinNamedPipeStream()
mWriteClosed(false),
mIsServer(false),
mIsConnected(false)
-{
+{ }
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::WinNamedPipeStream(HANDLE)
+// Purpose: Constructor (with already-connected pipe handle)
+// Created: 2008/10/01
+//
+// --------------------------------------------------------------------------
+WinNamedPipeStream::WinNamedPipeStream(HANDLE hNamedPipe)
+ : mSocketHandle(hNamedPipe),
+ mReadableEvent(INVALID_HANDLE_VALUE),
+ mBytesInBuffer(0),
+ mReadClosed(false),
+ mWriteClosed(false),
+ mIsServer(true),
+ mIsConnected(true)
+{
+ // create the Readable event
+ mReadableEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (mReadableEvent == INVALID_HANDLE_VALUE)
+ {
+ BOX_ERROR("Failed to create the Readable event: " <<
+ GetErrorMessage(GetLastError()));
+ Close();
+ THROW_EXCEPTION(CommonException, Internal)
+ }
+
+ // initialise the OVERLAPPED structure
+ memset(&mReadOverlap, 0, sizeof(mReadOverlap));
+ mReadOverlap.hEvent = mReadableEvent;
+
+ // start the first overlapped read
+ if (!ReadFile(mSocketHandle, mReadBuffer, sizeof(mReadBuffer),
+ NULL, &mReadOverlap))
+ {
+ DWORD err = GetLastError();
+
+ if (err != ERROR_IO_PENDING)
+ {
+ BOX_ERROR("Failed to start overlapped read: " <<
+ GetErrorMessage(err));
+ Close();
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketReadError)
+ }
+ }
}
// --------------------------------------------------------------------------
@@ -80,33 +128,17 @@ WinNamedPipeStream::~WinNamedPipeStream()
// Created: 2005/12/07
//
// --------------------------------------------------------------------------
-void WinNamedPipeStream::Accept(const std::string& rName)
+/*
+void WinNamedPipeStream::Accept()
{
- if (mSocketHandle != INVALID_HANDLE_VALUE || mIsConnected)
+ if (mSocketHandle == INVALID_HANDLE_VALUE)
{
- THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
+ THROW_EXCEPTION(ServerException, BadSocketHandle);
}
- std::string socket = sPipeNamePrefix + rName;
-
- mSocketHandle = CreateNamedPipeA(
- socket.c_str(), // pipe name
- PIPE_ACCESS_DUPLEX | // read/write access
- FILE_FLAG_OVERLAPPED, // enabled overlapped I/O
- PIPE_TYPE_BYTE | // message type pipe
- PIPE_READMODE_BYTE | // message-read mode
- PIPE_WAIT, // blocking mode
- 1, // max. instances
- 4096, // output buffer size
- 4096, // input buffer size
- NMPWAIT_USE_DEFAULT_WAIT, // client time-out
- NULL); // default security attribute
-
- if (mSocketHandle == INVALID_HANDLE_VALUE)
+ if (mIsConnected)
{
- BOX_ERROR("Failed to CreateNamedPipeA(" << socket << "): " <<
- GetErrorMessage(GetLastError()));
- THROW_EXCEPTION(ServerException, SocketOpenError)
+ THROW_EXCEPTION(ServerException, SocketAlreadyOpen);
}
bool connected = ConnectNamedPipe(mSocketHandle, (LPOVERLAPPED) NULL);
@@ -156,6 +188,7 @@ void WinNamedPipeStream::Accept(const std::string& rName)
}
}
}
+*/
// --------------------------------------------------------------------------
//
@@ -217,7 +250,7 @@ void WinNamedPipeStream::Connect(const std::string& rName)
int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
{
// TODO no support for timeouts yet
- if (Timeout != IOStream::TimeOutInfinite)
+ if (!mIsServer && Timeout != IOStream::TimeOutInfinite)
{
THROW_EXCEPTION(CommonException, AssertFailed)
}
@@ -249,8 +282,29 @@ int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
{
// overlapped I/O completed successfully?
// (wait if needed)
+ DWORD waitResult = WaitForSingleObject(
+ mReadOverlap.hEvent, Timeout);
- if (GetOverlappedResult(mSocketHandle,
+ if (waitResult == WAIT_ABANDONED)
+ {
+ BOX_ERROR("Wait for command socket read "
+ "abandoned by system");
+ THROW_EXCEPTION(ServerException,
+ BadSocketHandle);
+ }
+ else if (waitResult == WAIT_TIMEOUT)
+ {
+ // wait timed out, nothing to read
+ NumBytesRead = 0;
+ }
+ else if (waitResult != WAIT_OBJECT_0)
+ {
+ BOX_ERROR("Failed to wait for command "
+ "socket read: unknown result " <<
+ waitResult);
+ }
+ // object is ready to read from
+ else if (GetOverlappedResult(mSocketHandle,
&mReadOverlap, &NumBytesRead, TRUE))
{
needAnotherRead = true;
@@ -267,7 +321,7 @@ int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
{
if (err == ERROR_BROKEN_PIPE)
{
- BOX_ERROR("Control client "
+ BOX_NOTICE("Control client "
"disconnected");
}
else
@@ -342,29 +396,6 @@ int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
Conn_SocketReadError)
}
}
-
- // If the read succeeded immediately, leave the event
- // signaled, so that we will be called again to process
- // the newly read data and start another overlapped read.
- if (needAnotherRead && !mReadClosed)
- {
- // leave signalled
- }
- else if (!needAnotherRead && mBytesInBuffer > 0)
- {
- // leave signalled
- }
- else
- {
- // nothing left to read, reset the event
- ResetEvent(mReadableEvent);
- // FIXME: a pending read could have signalled
- // the event (again) while we were busy reading.
- // that signal would be lost, and the reading
- // thread would block. Should be pretty obvious
- // if this happens in practice: control client
- // hangs.
- }
}
else
{
@@ -440,23 +471,21 @@ void WinNamedPipeStream::Write(const void *pBuffer, int NBytes)
if (!Success)
{
- DWORD err = GetLastError();
- BOX_ERROR("Failed to write to control socket: " <<
- GetErrorMessage(err));
- Close();
-
// ERROR_NO_DATA is a strange name for
- // "The pipe is being closed". No exception wanted.
+ // "The pipe is being closed".
- if (err == ERROR_NO_DATA)
- {
- return;
- }
- else
+ DWORD err = GetLastError();
+
+ if (err != ERROR_NO_DATA)
{
- THROW_EXCEPTION(ConnectionException,
- Conn_SocketWriteError)
+ BOX_ERROR("Failed to write to control "
+ "socket: " << GetErrorMessage(err));
}
+
+ Close();
+
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketWriteError)
}
NumBytesWrittenTotal += NumBytesWrittenThisTime;
diff --git a/lib/server/WinNamedPipeStream.h b/lib/server/WinNamedPipeStream.h
index 6acd48f6..386ff7e3 100644
--- a/lib/server/WinNamedPipeStream.h
+++ b/lib/server/WinNamedPipeStream.h
@@ -24,10 +24,11 @@ class WinNamedPipeStream : public IOStream
{
public:
WinNamedPipeStream();
+ WinNamedPipeStream(HANDLE hNamedPipe);
~WinNamedPipeStream();
// server side - create the named pipe and listen for connections
- void Accept(const std::string& rName);
+ // use WinNamedPipeListener to do this instead.
// client side - connect to a waiting server
void Connect(const std::string& rName);
@@ -40,9 +41,6 @@ public:
virtual void Close();
virtual bool StreamDataLeft();
virtual bool StreamClosed();
- bool IsConnected() { return mIsConnected; }
- HANDLE GetSocketHandle() { return mSocketHandle; }
- HANDLE GetReadableEvent() { return mReadableEvent; }
protected:
void MarkAsReadClosed() {mReadClosed = true;}
@@ -62,6 +60,7 @@ private:
bool mIsServer;
bool mIsConnected;
+public:
static std::string sPipeNamePrefix;
};
diff --git a/lib/server/makeprotocol.pl.in b/lib/server/makeprotocol.pl.in
index 269ff3f5..91ba55b0 100755
--- a/lib/server/makeprotocol.pl.in
+++ b/lib/server/makeprotocol.pl.in
@@ -178,6 +178,9 @@ print CPP <<__E;
// Auto-generated file -- do not edit
#include "Box.h"
+
+#include <sstream>
+
#include "$h_filename"
#include "IOStream.h"
@@ -273,7 +276,7 @@ __E
if($derive_objects_from ne 'ProtocolObject')
{
- # output a definition for the protocol object derviced class
+ # output a definition for the protocol object derived class
print H <<__E;
class ${protocol_name}ProtocolServer;
@@ -338,6 +341,7 @@ __E
if(obj_is_type($cmd,'IsError'))
{
print H "\tbool IsError(int &rTypeOut, int &rSubTypeOut) const;\n";
+ print H "\tstd::string GetMessage() const;\n";
}
if($type eq 'Server' && obj_is_type($cmd, 'Command'))
{
@@ -498,6 +502,27 @@ bool ${class}IsError(int &rTypeOut, int &rSubTypeOut) const
rSubTypeOut = m$mem_subtype;
return true;
}
+std::string ${class}GetMessage() const
+{
+ switch(m$mem_subtype)
+ {
+__E
+ foreach my $const (@{$cmd_constants{$cmd}})
+ {
+ next unless $const =~ /^Err_(.*)/;
+ my $shortname = $1;
+ $const =~ s/ = .*//;
+ print CPP <<__E;
+ case $const: return "$shortname";
+__E
+ }
+ print CPP <<__E;
+ default:
+ std::ostringstream out;
+ out << "Unknown subtype " << m$mem_subtype;
+ return out.str();
+ }
+}
__E
}
@@ -513,11 +538,13 @@ __E
}
if($implement_filelog)
{
- my ($format,$args) = make_log_strings($cmd);
+ my ($log) = make_log_strings_framework($cmd);
print CPP <<__E;
void ${class}LogFile(const char *Action, FILE *File) const
{
- ::fprintf(File,"%s $format\\n",Action$args);
+ std::ostringstream oss;
+ oss << $log;
+ ::fprintf(File, "%s\\n", oss.str().c_str());
::fflush(File);
}
__E
@@ -620,16 +647,16 @@ protected:
__E
-my $construtor_extra = '';
-$construtor_extra .= ', mLogToSysLog(false)' if $implement_syslog;
-$construtor_extra .= ', mLogToFile(0)' if $implement_filelog;
+my $constructor_extra = '';
+$constructor_extra .= ', mLogToSysLog(false)' if $implement_syslog;
+$constructor_extra .= ', mLogToFile(0)' if $implement_filelog;
my $destructor_extra = ($type eq 'Server')?"\n\tDeleteStreamsToSend();":'';
my $prefix = $classname_base.'::';
print CPP <<__E;
$prefix$classname_base(IOStream &rStream)
- : Protocol(rStream)$construtor_extra
+ : Protocol(rStream)$constructor_extra
{
}
$prefix~$classname_base()
@@ -661,7 +688,7 @@ print CPP <<__E;
}
}
__E
-# write receieve and send functions
+# write receive and send functions
print CPP <<__E;
std::auto_ptr<$derive_objects_from> ${prefix}Receive()
{
@@ -734,51 +761,9 @@ void ${prefix}DoServer($context_class &rContext)
// Get an object from the conversation
std::auto_ptr<${derive_objects_from}> pobj(Receive());
-__E
- if($implement_syslog)
- {
- print CPP <<__E;
- if(mLogToSysLog)
- {
- pobj->LogSysLog("Receive");
- }
-__E
- }
- if($implement_filelog)
- {
- print CPP <<__E;
- if(mLogToFile != 0)
- {
- pobj->LogFile("Receive", mLogToFile);
- }
-__E
- }
- print CPP <<__E;
-
// Run the command
std::auto_ptr<${derive_objects_from}> preply((${derive_objects_from}*)(pobj->DoCommand(*this, rContext).release()));
-__E
- if($implement_syslog)
- {
- print CPP <<__E;
- if(mLogToSysLog)
- {
- preply->LogSysLog("Send");
- }
-__E
- }
- if($implement_filelog)
- {
- print CPP <<__E;
- if(mLogToFile != 0)
- {
- preply->LogFile("Send", mLogToFile);
- }
-__E
- }
- print CPP <<__E;
-
// Send the reply
Send(*(preply.get()));
@@ -824,13 +809,57 @@ if($implement_filelog || $implement_syslog)
if($implement_syslog)
{
- $fR .= qq~\tif(mLogToSysLog) { ::syslog(LOG_INFO, (Size==Protocol::ProtocolStream_SizeUncertain)?"Receiving stream, size uncertain":"Receiving stream, size %d", Size); }\n~;
- $fS .= qq~\tif(mLogToSysLog) { ::syslog(LOG_INFO, (Size==Protocol::ProtocolStream_SizeUncertain)?"Sending stream, size uncertain":"Sending stream, size %d", Size); }\n~;
+ $fR .= <<__E;
+ if(mLogToSysLog)
+ {
+ if(Size==Protocol::ProtocolStream_SizeUncertain)
+ {
+ BOX_TRACE("Receiving stream, size uncertain");
+ }
+ else
+ {
+ BOX_TRACE("Receiving stream, size " << Size);
+ }
}
+__E
+
+ $fS .= <<__E;
+ if(mLogToSysLog)
+ {
+ if(Size==Protocol::ProtocolStream_SizeUncertain)
+ {
+ BOX_TRACE("Sending stream, size uncertain");
+ }
+ else
+ {
+ BOX_TRACE("Sending stream, size " << Size);
+ }
+ }
+__E
+ }
+
if($implement_filelog)
{
- $fR .= qq~\tif(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain)?"Receiving stream, size uncertain\\n":"Receiving stream, size %d\\n", Size); ::fflush(mLogToFile); }\n~;
- $fS .= qq~\tif(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain)?"Sending stream, size uncertain\\n":"Sending stream, size %d\\n", Size); ::fflush(mLogToFile); }\n~;
+ $fR .= <<__E;
+ if(mLogToFile)
+ {
+ ::fprintf(mLogToFile,
+ (Size==Protocol::ProtocolStream_SizeUncertain)
+ ?"Receiving stream, size uncertain\\n"
+ :"Receiving stream, size %d\\n", Size);
+ ::fflush(mLogToFile);
+ }
+__E
+ $fS .= <<__E;
+ if(mLogToFile)
+ {
+ ::fprintf(mLogToFile,
+ (Size==Protocol::ProtocolStream_SizeUncertain)
+ ?"Sending stream, size uncertain\\n"
+ :"Sending stream, size %d\\n", Size);
+ ::fflush(mLogToFile);
+ }
+__E
}
print CPP <<__E;
@@ -888,11 +917,15 @@ std::auto_ptr<$classname_base$reply> ${classname_base}::Query(const $classname_b
if(preply->IsError(type, subType))
{
SetError(type, subType);
- TRACE2("Protocol: Error received %d/%d\\n", type, subType);
+ BOX_WARNING("$cmd command failed: received error " <<
+ ((${classname_base}Error&)*preply).GetMessage());
}
else
{
SetError(Protocol::UnknownError, Protocol::UnknownError);
+ BOX_WARNING("$cmd command failed: received "
+ "unexpected response type " <<
+ preply->GetType());
}
// Throw an exception
@@ -1020,10 +1053,22 @@ sub make_log_strings_framework
my ($format,$arg) = @{$log_display_types{$ty}};
$arg =~ s/VAR/m$nm/g;
- if ($format =~ m'x$')
+ if ($format eq '\\"%s\\"')
+ {
+ $arg = "\"\\\"\" << $arg << \"\\\"\"";
+ }
+ elsif ($format =~ m'x$')
{
- $arg = "std::hex << std::showbase " .
- "<< $arg << std::dec";
+ # my $width = 0;
+ # $ty =~ /^int(\d+)$/ and $width = $1 / 4;
+ $arg = "($arg == 0 ? \"0x\" : \"\") " .
+ "<< std::hex " .
+ "<< std::showbase " .
+ # "<< std::setw($width) " .
+ # "<< std::setfill('0') " .
+ # "<< std::internal " .
+ "<< $arg " .
+ "<< std::dec";
}
push @args, $arg;