diff options
Diffstat (limited to 'lib/server')
-rw-r--r-- | lib/server/Daemon.cpp | 328 | ||||
-rw-r--r-- | lib/server/Daemon.h | 28 | ||||
-rw-r--r-- | lib/server/OverlappedIO.h | 42 | ||||
-rw-r--r-- | lib/server/Protocol.cpp | 23 | ||||
-rw-r--r-- | lib/server/ProtocolUncertainStream.cpp | 21 | ||||
-rw-r--r-- | lib/server/SSLLib.cpp | 44 | ||||
-rw-r--r-- | lib/server/SSLLib.h | 4 | ||||
-rw-r--r-- | lib/server/ServerControl.cpp | 227 | ||||
-rw-r--r-- | lib/server/ServerControl.h | 188 | ||||
-rw-r--r-- | lib/server/ServerStream.h | 22 | ||||
-rw-r--r-- | lib/server/ServerTLS.h | 12 | ||||
-rw-r--r-- | lib/server/Socket.cpp | 15 | ||||
-rw-r--r-- | lib/server/Socket.h | 6 | ||||
-rw-r--r-- | lib/server/SocketListen.h | 85 | ||||
-rw-r--r-- | lib/server/SocketStream.cpp | 101 | ||||
-rw-r--r-- | lib/server/SocketStream.h | 3 | ||||
-rw-r--r-- | lib/server/SocketStreamTLS.cpp | 21 | ||||
-rw-r--r-- | lib/server/SocketStreamTLS.h | 3 | ||||
-rw-r--r-- | lib/server/TLSContext.cpp | 14 | ||||
-rw-r--r-- | lib/server/WinNamedPipeListener.h | 232 | ||||
-rw-r--r-- | lib/server/WinNamedPipeStream.cpp | 153 | ||||
-rw-r--r-- | lib/server/WinNamedPipeStream.h | 7 | ||||
-rwxr-xr-x | lib/server/makeprotocol.pl.in | 161 |
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; |