diff options
Diffstat (limited to 'lib/server')
-rw-r--r-- | lib/server/Daemon.cpp | 169 | ||||
-rw-r--r-- | lib/server/Daemon.h | 12 | ||||
-rw-r--r-- | lib/server/Message.cpp | 125 | ||||
-rw-r--r-- | lib/server/Message.h | 69 | ||||
-rw-r--r-- | lib/server/Protocol.cpp | 99 | ||||
-rw-r--r-- | lib/server/Protocol.h | 52 | ||||
-rw-r--r-- | lib/server/SSLLib.cpp | 13 | ||||
-rw-r--r-- | lib/server/SSLLib.h | 3 | ||||
-rw-r--r-- | lib/server/ServerException.txt | 4 | ||||
-rw-r--r-- | lib/server/ServerStream.h | 35 | ||||
-rw-r--r-- | lib/server/Socket.cpp | 25 | ||||
-rw-r--r-- | lib/server/SocketListen.h | 63 | ||||
-rw-r--r-- | lib/server/SocketStream.cpp | 18 | ||||
-rw-r--r-- | lib/server/SocketStream.h | 11 | ||||
-rw-r--r-- | lib/server/SocketStreamTLS.cpp | 37 | ||||
-rw-r--r-- | lib/server/TLSContext.cpp | 20 | ||||
-rw-r--r-- | lib/server/TcpNice.cpp | 235 | ||||
-rw-r--r-- | lib/server/TcpNice.h | 174 | ||||
-rwxr-xr-x | lib/server/makeprotocol.pl.in | 897 |
19 files changed, 2061 insertions, 0 deletions
diff --git a/lib/server/Daemon.cpp b/lib/server/Daemon.cpp index 8b4f1d0c..9b96647b 100644 --- a/lib/server/Daemon.cpp +++ b/lib/server/Daemon.cpp @@ -25,6 +25,7 @@ #ifdef WIN32 #include <ws2tcpip.h> +<<<<<<< HEAD #endif #include <iostream> @@ -36,6 +37,26 @@ #include "UnixUser.h" #include "FileModificationTime.h" #include "Logging.h" +======= + #include <process.h> +#endif + +#include "depot.h" + +#include <iostream> + +#ifdef NEED_BOX_VERSION_H +# include "BoxVersion.h" +#endif + +#include "Configuration.h" +#include "Daemon.h" +#include "FileModificationTime.h" +#include "Guards.h" +#include "Logging.h" +#include "ServerException.h" +#include "UnixUser.h" +>>>>>>> 0.12 #include "Utils.h" #include "MemLeakFindOn.h" @@ -64,6 +85,10 @@ Daemon::Daemon() mKeepConsoleOpenAfterFork(false), #endif mHaveConfigFile(false), +<<<<<<< HEAD +======= + mLogFileLevel(Log::INVALID), +>>>>>>> 0.12 mAppName(DaemonName()) { // In debug builds, switch on assert failure logging to syslog @@ -100,15 +125,25 @@ std::string Daemon::GetOptionString() { return "c:" #ifndef WIN32 +<<<<<<< HEAD "DFK" #endif "hkPqQt:TUvVW:"; +======= + "DF" + #endif + "hkKo:O:PqQt:TUvVW:"; +>>>>>>> 0.12 } void Daemon::Usage() { std::cout << DaemonBanner() << "\n" +<<<<<<< HEAD +======= + "(built with QDBM " << dpversion << ")\n" +>>>>>>> 0.12 "\n" "Usage: " << mAppName << " [options] [config file]\n" "\n" @@ -121,6 +156,7 @@ void Daemon::Usage() " -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" +<<<<<<< HEAD #ifndef WIN32 " -K Stop writing log messages to console while daemon is running\n" " -P Show process ID (PID) in console output\n" @@ -133,6 +169,21 @@ void Daemon::Usage() " -t <tag> Tag console output with specified marker\n" " -T Timestamp console output\n" " -U Timestamp console output with microseconds\n"; +======= + " -K Stop writing log messages to console while daemon is running\n" + " -o <file> Log to a file, defaults to maximum verbosity\n" + " -O <level> Set file log verbosity to error/warning/notice/info/trace/everything\n" + " -P Show process ID (PID) in console output\n" + " -q Run more quietly, reduce verbosity level by one, can repeat\n" + " -Q Run at minimum verbosity, log nothing to console and system\n" + " -t <tag> Tag console output with specified marker\n" + " -T Timestamp console output\n" + " -U Timestamp console output with microseconds\n" + " -v Run more verbosely, increase verbosity level by one, can repeat\n" + " -V Run at maximum verbosity, log everything to console and system\n" + " -W <level> Set verbosity to error/warning/notice/info/trace/everything\n" + ; +>>>>>>> 0.12 } // -------------------------------------------------------------------------- @@ -171,6 +222,16 @@ int Daemon::ProcessOption(signed int option) break; #endif // !WIN32 +<<<<<<< HEAD +======= + case 'h': + { + Usage(); + return 2; + } + break; + +>>>>>>> 0.12 case 'k': { mKeepConsoleOpenAfterFork = true; @@ -183,10 +244,28 @@ int Daemon::ProcessOption(signed int option) } break; +<<<<<<< HEAD case 'h': { Usage(); return 2; +======= + case 'o': + { + mLogFile = optarg; + mLogFileLevel = Log::EVERYTHING; + } + break; + + case 'O': + { + mLogFileLevel = Logging::GetNamedLevel(optarg); + if (mLogFileLevel == Log::INVALID) + { + BOX_FATAL("Invalid logging level: " << optarg); + return 2; + } +>>>>>>> 0.12 } break; @@ -215,6 +294,28 @@ int Daemon::ProcessOption(signed int option) } break; +<<<<<<< HEAD +======= + case 't': + { + Logging::SetProgramName(optarg); + Console::SetShowTag(true); + } + break; + + case 'T': + { + Console::SetShowTime(true); + } + break; + + case 'U': + { + Console::SetShowTime(true); + Console::SetShowTimeMicros(true); + } + break; +>>>>>>> 0.12 case 'v': { @@ -240,12 +341,17 @@ int Daemon::ProcessOption(signed int option) mLogLevel = Logging::GetNamedLevel(optarg); if (mLogLevel == Log::INVALID) { +<<<<<<< HEAD BOX_FATAL("Invalid logging level"); +======= + BOX_FATAL("Invalid logging level: " << optarg); +>>>>>>> 0.12 return 2; } } break; +<<<<<<< HEAD case 't': { Logging::SetProgramName(optarg); @@ -266,6 +372,8 @@ int Daemon::ProcessOption(signed int option) } break; +======= +>>>>>>> 0.12 case '?': { BOX_FATAL("Unknown option on command line: " @@ -295,6 +403,7 @@ int Daemon::ProcessOption(signed int option) // Created: 2003/07/29 // // -------------------------------------------------------------------------- +<<<<<<< HEAD int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[]) { // Find filename of config file @@ -305,6 +414,41 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[]) mLogLevel = Log::NOTICE; // need an int to do math with #else mLogLevel = Log::INFO; // need an int to do math with +======= +int Daemon::Main(const std::string& rDefaultConfigFile, int argc, + const char *argv[]) +{ + // Find filename of config file + mConfigFileName = rDefaultConfigFile; + mAppName = argv[0]; + + int ret = ProcessOptions(argc, argv); + if (ret != 0) + { + return ret; + } + + return Main(mConfigFileName); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Daemon::ProcessOptions(int argc, const char *argv[]) +// Purpose: Parses command-line options. Useful when you have +// a local Daemon object and don't intend to fork() +// or call Main(). +// Created: 2008/11/04 +// +// -------------------------------------------------------------------------- + +int Daemon::ProcessOptions(int argc, const char *argv[]) +{ + #ifdef BOX_RELEASE_BUILD + mLogLevel = Log::NOTICE; + #else + mLogLevel = Log::INFO; +>>>>>>> 0.12 #endif if (argc == 2 && strcmp(argv[1], "/?") == 0) @@ -359,7 +503,17 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[]) Logging::FilterConsole((Log::Level)mLogLevel); Logging::FilterSyslog ((Log::Level)mLogLevel); +<<<<<<< HEAD return Main(mConfigFileName); +======= + if (mLogFileLevel != Log::INVALID) + { + mapLogFileLogger.reset( + new FileLogger(mLogFile, mLogFileLevel)); + } + + return 0; +>>>>>>> 0.12 } // -------------------------------------------------------------------------- @@ -381,7 +535,11 @@ bool Daemon::Configure(const std::string& rConfigFileName) try { +<<<<<<< HEAD if (!FileExists(rConfigFileName.c_str())) +======= + if (!FileExists(rConfigFileName)) +>>>>>>> 0.12 { BOX_FATAL("The main configuration file for " << DaemonName() << " was not found: " << @@ -969,9 +1127,20 @@ void Daemon::SetProcessTitle(const char *format, ...) char title[256]; ::vsnprintf(title, sizeof(title), format, args); +<<<<<<< HEAD // Set process title ::setproctitle("%s", title); +======= +#ifdef WIN32 + StringCchCatA(title, sizeof(title)," - " PACKAGE_NAME); + SetConsoleTitleA(title); +#else // !WIN32 + // Set process title + ::setproctitle("%s", title); +#endif + +>>>>>>> 0.12 #endif // HAVE_SETPROCTITLE } diff --git a/lib/server/Daemon.h b/lib/server/Daemon.h index a3212a00..f2fa98c3 100644 --- a/lib/server/Daemon.h +++ b/lib/server/Daemon.h @@ -40,8 +40,14 @@ private: Daemon(const Daemon &rToCopy); public: +<<<<<<< HEAD virtual int Main(const char *DefaultConfigFile, int argc, const char *argv[]); +======= + virtual int Main(const std::string& rDefaultConfigFile, int argc, + const char *argv[]); + virtual int ProcessOptions(int argc, const char *argv[]); +>>>>>>> 0.12 /* override this Main() if you want custom option processing: */ virtual int Main(const std::string &rConfigFile); @@ -99,6 +105,12 @@ private: bool mKeepConsoleOpenAfterFork; bool mHaveConfigFile; int mLogLevel; // need an int to do math with +<<<<<<< HEAD +======= + std::string mLogFile; + Log::Level mLogFileLevel; + std::auto_ptr<FileLogger> mapLogFileLogger; +>>>>>>> 0.12 static Daemon *spDaemon; std::string mAppName; }; diff --git a/lib/server/Message.cpp b/lib/server/Message.cpp new file mode 100644 index 00000000..2ff9e6ae --- /dev/null +++ b/lib/server/Message.cpp @@ -0,0 +1,125 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Message.h +// Purpose: Protocol object base class +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "Message.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: Message::Message() +// Purpose: Default constructor +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +Message::Message() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Message::Message() +// Purpose: Destructor +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +Message::~Message() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Message::Message() +// Purpose: Copy constructor +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +Message::Message(const Message &rToCopy) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Message::IsError(int &, int &) +// Purpose: Does this represent an error, and if so, what is the type and subtype? +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +bool Message::IsError(int &rTypeOut, int &rSubTypeOut) const +{ + return false; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Message::IsConversationEnd() +// Purpose: Does this command end the conversation? +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +bool Message::IsConversationEnd() const +{ + return false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Message::GetType() +// Purpose: Return type of the object +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +int Message::GetType() const +{ + // This isn't implemented in the base class! + THROW_EXCEPTION(CommonException, Internal) +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Message::SetPropertiesFromStreamData(Protocol &) +// Purpose: Set the properties of the object from the stream data ready in the Protocol object +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +void Message::SetPropertiesFromStreamData(Protocol &rProtocol) +{ + // This isn't implemented in the base class! + THROW_EXCEPTION(CommonException, Internal) +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Message::WritePropertiesToStreamData(Protocol &) +// Purpose: Write the properties of the object into the stream data in the Protocol object +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +void Message::WritePropertiesToStreamData(Protocol &rProtocol) const +{ + // This isn't implemented in the base class! + THROW_EXCEPTION(CommonException, Internal) +} + + + diff --git a/lib/server/Message.h b/lib/server/Message.h new file mode 100644 index 00000000..0d073d49 --- /dev/null +++ b/lib/server/Message.h @@ -0,0 +1,69 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Message.h +// Purpose: Protocol object base class +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- + +#ifndef PROTOCOLOBJECT__H +#define PROTOCOLOBJECT__H + +#include <memory> + +class Protocol; +class ProtocolContext; + +// -------------------------------------------------------------------------- +// +// Class +// Name: Message +// Purpose: Basic object representation of objects to pass through a Protocol session +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +class Message +{ +public: + Message(); + virtual ~Message(); + Message(const Message &rToCopy); + + // Info about this object + virtual int GetType() const; + virtual bool IsError(int &rTypeOut, int &rSubTypeOut) const; + virtual bool IsConversationEnd() const; + + // reading and writing with Protocol objects + virtual void SetPropertiesFromStreamData(Protocol &rProtocol); + virtual void WritePropertiesToStreamData(Protocol &rProtocol) const; + + virtual void LogSysLog(const char *Action) const { } + virtual void LogFile(const char *Action, FILE *file) const { } +}; + +/* +class Reply; + +class Request : public Message +{ +public: + Request() { } + virtual ~Request() { } + Request(const Request &rToCopy) { } + virtual std::auto_ptr<Reply> DoCommand(Protocol &rProtocol, + ProtocolContext &rContext) = 0; +}; + +class Reply : public Message +{ +public: + Reply() { } + virtual ~Reply() { } + Reply(const Reply &rToCopy) { } +}; +*/ + +#endif // PROTOCOLOBJECT__H + diff --git a/lib/server/Protocol.cpp b/lib/server/Protocol.cpp index 5dc5d0b1..6333b1db 100644 --- a/lib/server/Protocol.cpp +++ b/lib/server/Protocol.cpp @@ -11,8 +11,14 @@ #include <sys/types.h> +<<<<<<< HEAD #include <stdlib.h> #include <string.h> +======= +#include <cstdlib> +#include <cstring> +#include <cstdio> +>>>>>>> 0.12 #include <new> @@ -44,6 +50,7 @@ // // -------------------------------------------------------------------------- Protocol::Protocol(IOStream &rStream) +<<<<<<< HEAD : mrStream(rStream), mHandshakeDone(false), mMaxObjectSize(PROTOCOL_DEFAULT_MAXOBJSIZE), @@ -55,6 +62,19 @@ Protocol::Protocol(IOStream &rStream) mValidDataSize(-1), mLastErrorType(NoError), mLastErrorSubType(NoError) +======= +: mrStream(rStream), + mHandshakeDone(false), + mMaxObjectSize(PROTOCOL_DEFAULT_MAXOBJSIZE), + mTimeout(PROTOCOL_DEFAULT_TIMEOUT), + mpBuffer(0), + mBufferSize(0), + mReadOffset(-1), + mWriteOffset(-1), + mValidDataSize(-1), + mLogToSysLog(false), + mLogToFile(NULL) +>>>>>>> 0.12 { BOX_TRACE("Send block allocation size is " << PROTOCOL_ALLOCATE_SEND_BLOCK_CHUNK); @@ -82,6 +102,7 @@ Protocol::~Protocol() // -------------------------------------------------------------------------- // // Function +<<<<<<< HEAD // Name: Protocol::GetLastError(int &, int &) // Purpose: Returns true if there was an error, and type and subtype if there was. // Created: 2003/08/19 @@ -110,6 +131,8 @@ bool Protocol::GetLastError(int &rTypeOut, int &rSubTypeOut) // -------------------------------------------------------------------------- // // Function +======= +>>>>>>> 0.12 // Name: Protocol::Handshake() // Purpose: Handshake with peer (exchange ident strings) // Created: 2003/08/20 @@ -127,7 +150,11 @@ void Protocol::Handshake() PW_Handshake hsSend; ::memset(&hsSend, 0, sizeof(hsSend)); // Copy in ident string +<<<<<<< HEAD ::strncpy(hsSend.mIdent, GetIdentString(), sizeof(hsSend.mIdent)); +======= + ::strncpy(hsSend.mIdent, GetProtocolIdentString(), sizeof(hsSend.mIdent)); +>>>>>>> 0.12 // Send it mrStream.Write(&hsSend, sizeof(hsSend)); @@ -200,7 +227,11 @@ void Protocol::CheckAndReadHdr(void *hdr) // Created: 2003/08/19 // // -------------------------------------------------------------------------- +<<<<<<< HEAD std::auto_ptr<ProtocolObject> Protocol::Receive() +======= +std::auto_ptr<Message> Protocol::ReceiveInternal() +>>>>>>> 0.12 { // Get object header PW_ObjectHeader objHeader; @@ -220,7 +251,11 @@ std::auto_ptr<ProtocolObject> Protocol::Receive() } // Create a blank object +<<<<<<< HEAD std::auto_ptr<ProtocolObject> obj(MakeProtocolObject(ntohl(objHeader.mObjType))); +======= + std::auto_ptr<Message> obj(MakeMessage(ntohl(objHeader.mObjType))); +>>>>>>> 0.12 // Make sure memory is allocated to read it into EnsureBufferAllocated(objSize); @@ -272,7 +307,11 @@ std::auto_ptr<ProtocolObject> Protocol::Receive() // Created: 2003/08/19 // // -------------------------------------------------------------------------- +<<<<<<< HEAD void Protocol::Send(const ProtocolObject &rObject) +======= +void Protocol::SendInternal(const Message &rObject) +>>>>>>> 0.12 { // Check usage if(mValidDataSize != -1 || mWriteOffset != -1 || mReadOffset != -1) @@ -715,6 +754,20 @@ void Protocol::SendStream(IOStream &rStream) // Can't send this using the fixed size header uncertainSize = true; } +<<<<<<< HEAD +======= + + if(streamSize == 0) + { + // Server protocol will throw an assertion failure if we + // try to send a stream whose size is definitely zero: + // ASSERT FAILED: [BytesToRead > 0] at PartialReadStream.cpp:31 + // so catch this on the client side to help debugging + THROW_EXCEPTION_MESSAGE(ServerException, Protocol_BadUsage, + "Sending a stream with a definite size of zero " + "is not allowed in the protocol"); + } +>>>>>>> 0.12 // Inform sub class InformStreamSending(streamSize); @@ -854,7 +907,30 @@ int Protocol::SendStreamSendBlock(uint8_t *Block, int BytesInBlock) // -------------------------------------------------------------------------- void Protocol::InformStreamReceiving(u_int32_t Size) { +<<<<<<< HEAD // Do nothing +======= + if(GetLogToSysLog()) + { + if(Size == Protocol::ProtocolStream_SizeUncertain) + { + BOX_TRACE("Receiving stream, size uncertain"); + } + else + { + BOX_TRACE("Receiving stream, size " << Size); + } + } + + if(GetLogToFile()) + { + ::fprintf(GetLogToFile(), + (Size == Protocol::ProtocolStream_SizeUncertain) + ? "Receiving stream, size uncertain\n" + : "Receiving stream, size %d\n", Size); + ::fflush(GetLogToFile()); + } +>>>>>>> 0.12 } // -------------------------------------------------------------------------- @@ -867,7 +943,30 @@ void Protocol::InformStreamReceiving(u_int32_t Size) // -------------------------------------------------------------------------- void Protocol::InformStreamSending(u_int32_t Size) { +<<<<<<< HEAD // Do nothing +======= + if(GetLogToSysLog()) + { + if(Size == Protocol::ProtocolStream_SizeUncertain) + { + BOX_TRACE("Sending stream, size uncertain"); + } + else + { + BOX_TRACE("Sending stream, size " << Size); + } + } + + if(GetLogToFile()) + { + ::fprintf(GetLogToFile(), + (Size == Protocol::ProtocolStream_SizeUncertain) + ? "Sending stream, size uncertain\n" + : "Sending stream, size %d\n", Size); + ::fflush(GetLogToFile()); + } +>>>>>>> 0.12 } diff --git a/lib/server/Protocol.h b/lib/server/Protocol.h index e037e33c..dc660ad5 100644 --- a/lib/server/Protocol.h +++ b/lib/server/Protocol.h @@ -12,12 +12,22 @@ #include <sys/types.h> +<<<<<<< HEAD class IOStream; #include "ProtocolObject.h" +======= +>>>>>>> 0.12 #include <memory> #include <vector> #include <string> +<<<<<<< HEAD +======= +#include "Message.h" + +class IOStream; + +>>>>>>> 0.12 // default timeout is 15 minutes #define PROTOCOL_DEFAULT_TIMEOUT (15*60*1000) // 16 default maximum object size -- should be enough @@ -40,11 +50,22 @@ public: private: Protocol(const Protocol &rToCopy); +<<<<<<< HEAD public: void Handshake(); std::auto_ptr<ProtocolObject> Receive(); void Send(const ProtocolObject &rObject); +======= +protected: + // Unsafe to make public, as they may allow sending objects + // from a different protocol. The derived class prevents this. + std::auto_ptr<Message> ReceiveInternal(); + void SendInternal(const Message &rObject); + +public: + void Handshake(); +>>>>>>> 0.12 std::auto_ptr<IOStream> ReceiveStream(); void SendStream(IOStream &rStream); @@ -54,8 +75,11 @@ public: UnknownError = 0 }; +<<<<<<< HEAD bool GetLastError(int &rTypeOut, int &rSubTypeOut); +======= +>>>>>>> 0.12 // -------------------------------------------------------------------------- // // Function @@ -87,7 +111,11 @@ public: // -------------------------------------------------------------------------- void SetMaxObjectSize(unsigned int NewMaxObjSize) {mMaxObjectSize = NewMaxObjSize;} +<<<<<<< HEAD // For ProtocolObject derived classes +======= + // For Message derived classes +>>>>>>> 0.12 void Read(void *Buffer, int Size); void Read(std::string &rOut, int Size); void Read(int64_t &rOut); @@ -168,11 +196,23 @@ public: { ProtocolStream_SizeUncertain = 0xffffffff }; +<<<<<<< HEAD protected: virtual std::auto_ptr<ProtocolObject> MakeProtocolObject(int ObjType) = 0; virtual const char *GetIdentString() = 0; void SetError(int Type, int SubType) {mLastErrorType = Type; mLastErrorSubType = SubType;} +======= + bool GetLogToSysLog() { return mLogToSysLog; } + FILE *GetLogToFile() { return mLogToFile; } + void SetLogToSysLog(bool Log = false) {mLogToSysLog = Log;} + void SetLogToFile(FILE *File = 0) {mLogToFile = File;} + +protected: + virtual std::auto_ptr<Message> MakeMessage(int ObjType) = 0; + virtual const char *GetProtocolIdentString() = 0; + +>>>>>>> 0.12 void CheckAndReadHdr(void *hdr); // don't use type here to avoid dependency // Will be used for logging @@ -183,7 +223,10 @@ private: void EnsureBufferAllocated(int Size); int SendStreamSendBlock(uint8_t *Block, int BytesInBlock); +<<<<<<< HEAD private: +======= +>>>>>>> 0.12 IOStream &mrStream; bool mHandshakeDone; unsigned int mMaxObjectSize; @@ -193,8 +236,17 @@ private: int mReadOffset; int mWriteOffset; int mValidDataSize; +<<<<<<< HEAD int mLastErrorType; int mLastErrorSubType; +======= + bool mLogToSysLog; + FILE *mLogToFile; +}; + +class ProtocolContext +{ +>>>>>>> 0.12 }; #endif // PROTOCOL__H diff --git a/lib/server/SSLLib.cpp b/lib/server/SSLLib.cpp index de7a941b..6feaae4a 100644 --- a/lib/server/SSLLib.cpp +++ b/lib/server/SSLLib.cpp @@ -18,6 +18,10 @@ #include <wincrypt.h> #endif +<<<<<<< HEAD +======= +#include "CryptoUtils.h" +>>>>>>> 0.12 #include "SSLLib.h" #include "ServerException.h" @@ -39,8 +43,14 @@ void SSLLib::Initialise() { if(!::SSL_library_init()) { +<<<<<<< HEAD LogError("initialising OpenSSL"); THROW_EXCEPTION(ServerException, SSLLibraryInitialisationError) +======= + THROW_EXCEPTION_MESSAGE(ServerException, + SSLLibraryInitialisationError, + CryptoUtils::LogError("initialising OpenSSL")); +>>>>>>> 0.12 } // More helpful error messages @@ -89,6 +99,7 @@ void SSLLib::Initialise() } +<<<<<<< HEAD // -------------------------------------------------------------------------- // // Function @@ -109,3 +120,5 @@ void SSLLib::LogError(const std::string& rErrorDuringAction) } } +======= +>>>>>>> 0.12 diff --git a/lib/server/SSLLib.h b/lib/server/SSLLib.h index ff4aab19..b679d623 100644 --- a/lib/server/SSLLib.h +++ b/lib/server/SSLLib.h @@ -29,7 +29,10 @@ namespace SSLLib { void Initialise(); +<<<<<<< HEAD void LogError(const std::string& rErrorDuringAction); +======= +>>>>>>> 0.12 }; #endif // SSLLIB__H diff --git a/lib/server/ServerException.txt b/lib/server/ServerException.txt index ed591b73..f8c558c6 100644 --- a/lib/server/ServerException.txt +++ b/lib/server/ServerException.txt @@ -13,7 +13,11 @@ SocketOpenError 10 SocketPollError 11 SocketCloseError 13 SocketNameUNIXPathTooLong 14 +<<<<<<< HEAD SocketBindError 16 Check the ListenAddresses directive in your config file -- must refer to local IP addresses only +======= +SocketBindError 16 Check the ListenAddresses directive (bbstored) or CommandSocket (bbackupd) in your config file -- must refer to local IP addresses (or existing writable path) only +>>>>>>> 0.12 SocketAcceptError 17 ServerStreamBadListenAddrs 18 ServerForkError 19 diff --git a/lib/server/ServerStream.h b/lib/server/ServerStream.h index e49dbcbe..8625832d 100644 --- a/lib/server/ServerStream.h +++ b/lib/server/ServerStream.h @@ -48,6 +48,18 @@ private: ServerStream(const ServerStream &rToCopy) { } +<<<<<<< HEAD +======= + + std::string mConnectionDetails; + +protected: + const std::string& GetConnectionDetails() + { + return mConnectionDetails; + } + +>>>>>>> 0.12 public: virtual const char *DaemonName() const @@ -122,6 +134,13 @@ public: protected: virtual void NotifyListenerIsReady() { } +<<<<<<< HEAD +======= + virtual void LogConnectionDetails(std::string details) + { + BOX_NOTICE("Handling incoming connection from " << details); + } +>>>>>>> 0.12 public: virtual void Run2(bool &rChildExit) @@ -237,8 +256,14 @@ public: { // Get the incoming connection // (with zero wait time) +<<<<<<< HEAD std::string logMessage; std::auto_ptr<StreamType> connection(psocket->Accept(0, &logMessage)); +======= + std::auto_ptr<StreamType> connection( + psocket->Accept(0, + &mConnectionDetails)); +>>>>>>> 0.12 // Was there one (there should be...) if(connection.get()) @@ -264,6 +289,10 @@ public: // Set up daemon EnterChild(); SetProcessTitle("transaction"); +<<<<<<< HEAD +======= + LogConnectionDetails(mConnectionDetails); +>>>>>>> 0.12 // Memory leak test the forked process #ifdef BOX_MEMORY_LEAK_TESTING @@ -281,7 +310,13 @@ public: } // Log it +<<<<<<< HEAD BOX_NOTICE("Message from child process " << pid << ": " << logMessage); +======= + BOX_TRACE("Forked child process " << pid << + " to handle connection from " << + mConnectionDetails); +>>>>>>> 0.12 } else { diff --git a/lib/server/Socket.cpp b/lib/server/Socket.cpp index 4a83bdb0..69a11330 100644 --- a/lib/server/Socket.cpp +++ b/lib/server/Socket.cpp @@ -123,6 +123,7 @@ void Socket::NameLookupToSockAddr(SocketAllAddr &addr, int &sockDomain, // -------------------------------------------------------------------------- void Socket::LogIncomingConnection(const struct sockaddr *addr, socklen_t addrlen) { +<<<<<<< HEAD if(addr == NULL) {THROW_EXCEPTION(CommonException, BadArguments)} switch(addr->sa_family) @@ -144,6 +145,10 @@ void Socket::LogIncomingConnection(const struct sockaddr *addr, socklen_t addrle BOX_WARNING("Incoming connection of unknown type"); break; } +======= + BOX_INFO("Incoming connection from " << + IncomingConnectionLogMessage(addr, addrlen)); +>>>>>>> 0.12 } // -------------------------------------------------------------------------- @@ -161,20 +166,40 @@ std::string Socket::IncomingConnectionLogMessage(const struct sockaddr *addr, so switch(addr->sa_family) { case AF_UNIX: +<<<<<<< HEAD return std::string("Incoming connection from local (UNIX socket)"); +======= + return std::string("local (UNIX socket)"); +>>>>>>> 0.12 break; case AF_INET: { +<<<<<<< HEAD char msg[256]; // more than enough sockaddr_in *a = (sockaddr_in*)addr; sprintf(msg, "Incoming connection from %s port %d", inet_ntoa(a->sin_addr), ntohs(a->sin_port)); return std::string(msg); +======= + sockaddr_in *a = (sockaddr_in*)addr; + std::ostringstream oss; + oss << inet_ntoa(a->sin_addr) << " port " << + ntohs(a->sin_port); + return oss.str(); +>>>>>>> 0.12 } break; default: +<<<<<<< HEAD return std::string("Incoming connection of unknown type"); +======= + { + std::ostringstream oss; + oss << "unknown socket type " << addr->sa_family; + return oss.str(); + } +>>>>>>> 0.12 break; } diff --git a/lib/server/SocketListen.h b/lib/server/SocketListen.h index 586adf22..635b15e8 100644 --- a/lib/server/SocketListen.h +++ b/lib/server/SocketListen.h @@ -87,12 +87,24 @@ public: { Close(); } +<<<<<<< HEAD +======= + +>>>>>>> 0.12 private: SocketListen(const SocketListen &rToCopy) { } +<<<<<<< HEAD public: +======= + + int mType, mPort; + std::string mName; + +public: +>>>>>>> 0.12 enum { MaxMultipleListenSockets = MaxMultiListenSockets @@ -108,8 +120,13 @@ public: if(::close(mSocketHandle) == -1) #endif { +<<<<<<< HEAD BOX_LOG_SYS_ERROR("Failed to close network " "socket"); +======= + BOX_LOG_SOCKET_ERROR(mType, mName, mPort, + "Failed to close network socket"); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, SocketCloseError) } @@ -127,6 +144,13 @@ public: // ------------------------------------------------------------------ void Listen(Socket::Type Type, const char *Name, int Port = 0) { +<<<<<<< HEAD +======= + mType = Type; + mName = Name; + mPort = Port; + +>>>>>>> 0.12 if(mSocketHandle != -1) { THROW_EXCEPTION(ServerException, SocketAlreadyOpen); @@ -144,7 +168,12 @@ public: 0 /* let OS choose protocol */); if(mSocketHandle == -1) { +<<<<<<< HEAD BOX_LOG_SYS_ERROR("Failed to create a network socket"); +======= + BOX_LOG_SOCKET_ERROR(Type, Name, Port, + "Failed to create a network socket"); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, SocketOpenError) } @@ -158,7 +187,12 @@ public: &option, sizeof(option)) == -1) #endif { +<<<<<<< HEAD BOX_LOG_SYS_ERROR("Failed to set socket options"); +======= + BOX_LOG_SOCKET_ERROR(Type, Name, Port, + "Failed to set socket options"); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, SocketOpenError) } @@ -166,10 +200,25 @@ public: if(::bind(mSocketHandle, &addr.sa_generic, addrLen) == -1 || ::listen(mSocketHandle, ListenBacklog) == -1) { +<<<<<<< HEAD // Dispose of the socket ::close(mSocketHandle); mSocketHandle = -1; THROW_EXCEPTION(ServerException, SocketBindError) +======= + int err_number = errno; + + BOX_LOG_SOCKET_ERROR(Type, Name, Port, + "Failed to bind socket"); + + // Dispose of the socket + ::close(mSocketHandle); + mSocketHandle = -1; + + THROW_SYS_FILE_ERRNO("Failed to bind or listen " + "on socket", Name, err_number, + ServerException, SocketBindError); +>>>>>>> 0.12 } } @@ -222,7 +271,11 @@ public: // signal? if(errno == EINTR) { +<<<<<<< HEAD BOX_ERROR("Failed to accept " +======= + BOX_INFO("Failed to accept " +>>>>>>> 0.12 "connection: interrupted by " "signal"); // return nothing @@ -230,8 +283,13 @@ public: } else { +<<<<<<< HEAD BOX_LOG_SYS_ERROR("Failed to poll " "connection"); +======= + BOX_LOG_SOCKET_ERROR(mType, mName, mPort, + "Failed to poll connection"); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, SocketPollError) } @@ -250,7 +308,12 @@ public: // Got socket (or error), unlock (implicit in destruction) if(sock == -1) { +<<<<<<< HEAD BOX_LOG_SYS_ERROR("Failed to accept connection"); +======= + BOX_LOG_SOCKET_ERROR(mType, mName, mPort, + "Failed to accept connection"); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, SocketAcceptError) } diff --git a/lib/server/SocketStream.cpp b/lib/server/SocketStream.cpp index 95b4b4f4..7c92ebba 100644 --- a/lib/server/SocketStream.cpp +++ b/lib/server/SocketStream.cpp @@ -154,14 +154,24 @@ void SocketStream::Open(Socket::Type Type, const std::string& rName, int Port) int sockDomain = 0; SocketAllAddr addr; int addrLen = 0; +<<<<<<< HEAD Socket::NameLookupToSockAddr(addr, sockDomain, Type, rName, Port, addrLen); +======= + Socket::NameLookupToSockAddr(addr, sockDomain, Type, rName, Port, + addrLen); +>>>>>>> 0.12 // Create the socket mSocketHandle = ::socket(sockDomain, SOCK_STREAM, 0 /* let OS choose protocol */); if(mSocketHandle == INVALID_SOCKET_VALUE) { +<<<<<<< HEAD BOX_LOG_SYS_ERROR("Failed to create a network socket"); +======= + BOX_LOG_SOCKET_ERROR(Type, rName, Port, + "Failed to create a network socket"); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, SocketOpenError) } @@ -169,6 +179,7 @@ void SocketStream::Open(Socket::Type Type, const std::string& rName, int Port) if(::connect(mSocketHandle, &addr.sa_generic, addrLen) == -1) { // Dispose of the socket +<<<<<<< HEAD #ifdef WIN32 DWORD err = WSAGetLastError(); ::closesocket(mSocketHandle); @@ -179,6 +190,13 @@ void SocketStream::Open(Socket::Type Type, const std::string& rName, int Port) BOX_LOG_SYS_ERROR("Failed to connect to socket (type " << Type << ", name " << rName << ", port " << Port << ")"); +======= + BOX_LOG_SOCKET_ERROR(Type, rName, Port, + "Failed to connect to socket"); +#ifdef WIN32 + ::closesocket(mSocketHandle); +#else // !WIN32 +>>>>>>> 0.12 ::close(mSocketHandle); #endif // WIN32 diff --git a/lib/server/SocketStream.h b/lib/server/SocketStream.h index 2b582f21..aa62e4e9 100644 --- a/lib/server/SocketStream.h +++ b/lib/server/SocketStream.h @@ -51,7 +51,10 @@ public: virtual bool GetPeerCredentials(uid_t &rUidOut, gid_t &rGidOut); protected: +<<<<<<< HEAD tOSSocketHandle GetSocketHandle(); +======= +>>>>>>> 0.12 void MarkAsReadClosed() {mReadClosed = true;} void MarkAsWriteClosed() {mWriteClosed = true;} @@ -69,6 +72,14 @@ public: off_t GetBytesWritten() const {return mBytesWritten;} void ResetCounters() {mBytesRead = mBytesWritten = 0;} bool IsOpened() { return mSocketHandle != INVALID_SOCKET_VALUE; } +<<<<<<< HEAD +======= + + /** + * Only for use by NiceSocketStream! + */ + tOSSocketHandle GetSocketHandle(); +>>>>>>> 0.12 }; #endif // SOCKETSTREAM__H diff --git a/lib/server/SocketStreamTLS.cpp b/lib/server/SocketStreamTLS.cpp index 19fdadd4..6f1cc46a 100644 --- a/lib/server/SocketStreamTLS.cpp +++ b/lib/server/SocketStreamTLS.cpp @@ -19,11 +19,20 @@ #include <poll.h> #endif +<<<<<<< HEAD #include "SocketStreamTLS.h" #include "SSLLib.h" #include "ServerException.h" #include "TLSContext.h" #include "BoxTime.h" +======= +#include "BoxTime.h" +#include "CryptoUtils.h" +#include "ServerException.h" +#include "SocketStreamTLS.h" +#include "SSLLib.h" +#include "TLSContext.h" +>>>>>>> 0.12 #include "MemLeakFindOn.h" @@ -124,7 +133,11 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) mpBIO = ::BIO_new(::BIO_s_socket()); if(mpBIO == 0) { +<<<<<<< HEAD SSLLib::LogError("creating socket bio"); +======= + CryptoUtils::LogError("creating socket bio"); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, TLSAllocationFailed) } @@ -135,7 +148,11 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) mpSSL = ::SSL_new(rContext.GetRawContext()); if(mpSSL == 0) { +<<<<<<< HEAD SSLLib::LogError("creating SSL object"); +======= + CryptoUtils::LogError("creating SSL object"); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, TLSAllocationFailed) } @@ -203,12 +220,20 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) // Error occured if(IsServer) { +<<<<<<< HEAD SSLLib::LogError("accepting connection"); +======= + CryptoUtils::LogError("accepting connection"); +>>>>>>> 0.12 THROW_EXCEPTION(ConnectionException, Conn_TLSHandshakeFailed) } else { +<<<<<<< HEAD SSLLib::LogError("connecting"); +======= + CryptoUtils::LogError("connecting"); +>>>>>>> 0.12 THROW_EXCEPTION(ConnectionException, Conn_TLSHandshakeFailed) } } @@ -335,7 +360,11 @@ int SocketStreamTLS::Read(void *pBuffer, int NBytes, int Timeout) break; default: +<<<<<<< HEAD SSLLib::LogError("reading"); +======= + CryptoUtils::LogError("reading"); +>>>>>>> 0.12 THROW_EXCEPTION(ConnectionException, Conn_TLSReadFailed) break; } @@ -400,7 +429,11 @@ void SocketStreamTLS::Write(const void *pBuffer, int NBytes) break; default: +<<<<<<< HEAD SSLLib::LogError("writing"); +======= + CryptoUtils::LogError("writing"); +>>>>>>> 0.12 THROW_EXCEPTION(ConnectionException, Conn_TLSWriteFailed) break; } @@ -442,7 +475,11 @@ void SocketStreamTLS::Shutdown(bool Read, bool Write) if(::SSL_shutdown(mpSSL) < 0) { +<<<<<<< HEAD SSLLib::LogError("shutting down"); +======= + CryptoUtils::LogError("shutting down"); +>>>>>>> 0.12 THROW_EXCEPTION(ConnectionException, Conn_TLSShutdownFailed) } diff --git a/lib/server/TLSContext.cpp b/lib/server/TLSContext.cpp index ebc7384a..1f06d602 100644 --- a/lib/server/TLSContext.cpp +++ b/lib/server/TLSContext.cpp @@ -12,7 +12,11 @@ #define TLS_CLASS_IMPLEMENTATION_CPP #include <openssl/ssl.h> +<<<<<<< HEAD #include "TLSContext.h" +======= +#include "CryptoUtils.h" +>>>>>>> 0.12 #include "ServerException.h" #include "SSLLib.h" #include "TLSContext.h" @@ -77,14 +81,22 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c { std::string msg = "loading certificates from "; msg += CertificatesFile; +<<<<<<< HEAD SSLLib::LogError(msg); +======= + CryptoUtils::LogError(msg); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, TLSLoadCertificatesFailed) } if(::SSL_CTX_use_PrivateKey_file(mpContext, PrivateKeyFile, SSL_FILETYPE_PEM) != 1) { std::string msg = "loading private key from "; msg += PrivateKeyFile; +<<<<<<< HEAD SSLLib::LogError(msg); +======= + CryptoUtils::LogError(msg); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, TLSLoadPrivateKeyFailed) } @@ -93,7 +105,11 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c { std::string msg = "loading CA cert from "; msg += TrustedCAsFile; +<<<<<<< HEAD SSLLib::LogError(msg); +======= + CryptoUtils::LogError(msg); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, TLSLoadTrustedCAsFailed) } @@ -105,7 +121,11 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c // Setup allowed ciphers if(::SSL_CTX_set_cipher_list(mpContext, CIPHER_LIST) != 1) { +<<<<<<< HEAD SSLLib::LogError("setting cipher list to " CIPHER_LIST); +======= + CryptoUtils::LogError("setting cipher list to " CIPHER_LIST); +>>>>>>> 0.12 THROW_EXCEPTION(ServerException, TLSSetCiphersFailed) } } diff --git a/lib/server/TcpNice.cpp b/lib/server/TcpNice.cpp new file mode 100644 index 00000000..20619e49 --- /dev/null +++ b/lib/server/TcpNice.cpp @@ -0,0 +1,235 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: TcpNice.cpp +// Purpose: Calculator for adaptive TCP window sizing to support +// low-priority background flows using the stochastic +// algorithm, as described in +// http://www.thlab.net/~lmassoul/p18-key.pdf +// Created: 11/02/2012 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include "TcpNice.h" +#include "Logging.h" +#include "BoxTime.h" + +#ifdef HAVE_NETINET_TCP_H +# include <netinet/tcp.h> +#endif + +#ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +# include <sys/socket.h> +#endif + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: TcpNice::TcpNice() +// Purpose: Initialise state of the calculator +// Created: 11/02/2012 +// +// -------------------------------------------------------------------------- +TcpNice::TcpNice() +: mLastWindowSize(1), + mGammaPercent(100), + mAlphaStar(100), + mDeltaPercent(10) +{ + mRateEstimateMovingAverage[0] = 0; + mRateEstimateMovingAverage[1] = 0; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: int GetNextWindowSize(int bytesChange, +// box_time_t timeElapsed, int rttEstimateMillis) +// Purpose: Calculate the next recommended window size, given the +// number of bytes sent since the previous recommendation, +// and the time elapsed. +// Created: 11/02/2012 +// +// -------------------------------------------------------------------------- +int TcpNice::GetNextWindowSize(int bytesChange, box_time_t timeElapsed, + int rttEstimateMicros) +{ + int epsilon = (mAlphaStar * 1000000) / rttEstimateMicros; + + // timeElapsed is in microseconds, so this will fail for T > 2000 seconds + int rateLastPeriod = ((uint64_t)bytesChange * 1000000 / timeElapsed); + + int rawAdjustment = epsilon + rateLastPeriod - + mRateEstimateMovingAverage[0]; + + int gammaAdjustment = (rawAdjustment * mGammaPercent) / 100; + + int newWindowSize = mLastWindowSize + gammaAdjustment; + + int newRateEstimateMovingAverage = + (((100 - mDeltaPercent) * mRateEstimateMovingAverage[1]) / 100) + + ((mDeltaPercent * rateLastPeriod) / 100); + + /* + * b is the number of bytes sent during the previous control period + * T is the length (in us) of the previous control period + * rtt is the round trip time (in us) reported by the kernel on the socket + * e is epsilon, a parameter of the formula, calculated as alpha/rtt + * rb is the actual rate (goodput) over the previous period + * rbhat is the previous (last-but-one) EWMA rate estimate + * raw is the unscaled adjustment to the window size + * gamma is the scaled adjustment to the window size + * wb is the final window size + */ + + BOX_TRACE("TcpNice: " + "b=" << bytesChange << ", " + "T=" << timeElapsed << ", " + "rtt=" << rttEstimateMicros << ", " + "e=" << epsilon << ", " + "rb=" << rateLastPeriod << ", " + "rbhat=" << newRateEstimateMovingAverage << ", " + "raw=" << rawAdjustment << ", " + "gamma=" << gammaAdjustment << ", " + "wb=" << newWindowSize); + + mRateEstimateMovingAverage[0] = mRateEstimateMovingAverage[1]; + mRateEstimateMovingAverage[1] = newRateEstimateMovingAverage; + mLastWindowSize = newWindowSize; + + return newWindowSize; +} + +// -------------------------------------------------------------------------- +// +// Constructor +// Name: NiceSocketStream::NiceSocketStream( +// std::auto_ptr<SocketStream> apSocket) +// Purpose: Initialise state of the socket wrapper +// Created: 11/02/2012 +// +// -------------------------------------------------------------------------- + +NiceSocketStream::NiceSocketStream(std::auto_ptr<SocketStream> apSocket) +: mapSocket(apSocket), + mTcpNice(), + mBytesWrittenThisPeriod(0), + mPeriodStartTime(GetCurrentBoxTime()), + mTimeIntervalMillis(1000), + mEnabled(false) +{ } + +// -------------------------------------------------------------------------- +// +// Function +// Name: NiceSocketStream::Write(const void *pBuffer, int NBytes) +// Purpose: Writes bytes to the underlying stream, adjusting window size +// using a TcpNice calculator. +// Created: 2012/02/11 +// +// -------------------------------------------------------------------------- +void NiceSocketStream::Write(const void *pBuffer, int NBytes) +{ +#if HAVE_DECL_SO_SNDBUF && HAVE_DECL_TCP_INFO + if(mEnabled && mapTimer.get() && mapTimer->HasExpired()) + { + box_time_t newPeriodStart = GetCurrentBoxTime(); + box_time_t elapsed = newPeriodStart - mPeriodStartTime; + int socket = mapSocket->GetSocketHandle(); + int rtt = 50; // WAG + +# if HAVE_DECL_SOL_TCP && HAVE_DECL_TCP_INFO && HAVE_STRUCT_TCP_INFO_TCPI_RTT + struct tcp_info info; + socklen_t optlen = sizeof(info); + if(getsockopt(socket, SOL_TCP, TCP_INFO, &info, &optlen) == -1) + { + BOX_LOG_SYS_WARNING("getsockopt(" << socket << ", SOL_TCP, " + "TCP_INFO) failed"); + } + else if(optlen != sizeof(info)) + { + BOX_WARNING("getsockopt(" << socket << ", SOL_TCP, " + "TCP_INFO) return structure size " << optlen << ", " + "expected " << sizeof(info)); + } + else + { + rtt = info.tcpi_rtt; + } +# endif + + int newWindow = mTcpNice.GetNextWindowSize(mBytesWrittenThisPeriod, + elapsed, rtt); + + if(setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &newWindow, + sizeof(newWindow)) == -1) + { + BOX_LOG_SYS_WARNING("getsockopt(" << socket << ", SOL_SOCKET, " + "SO_SNDBUF, " << newWindow << ") failed"); + } + + StopTimer(); + } + + if(mEnabled && !mapTimer.get()) + { + // Don't start the timer until we receive the first data to write, + // as diffing might take a long time and we don't want to bias + // the TcpNice algorithm by running while we don't have bulk data + // to send. + StartTimer(); + mPeriodStartTime = GetCurrentBoxTime(); + mBytesWrittenThisPeriod = 0; + } + + mBytesWrittenThisPeriod += NBytes; +#endif // HAVE_DECL_SO_SNDBUF + + mapSocket->Write(pBuffer, NBytes); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: NiceSocketStream::SetEnabled(bool enabled) +// Purpose: Update the enabled status, and if disabling, cancel the +// timer and set a sensible window size. +// Created: 2012/02/12 +// +// -------------------------------------------------------------------------- + +void NiceSocketStream::SetEnabled(bool enabled) +{ + mEnabled = enabled; + + if(!enabled) + { + StopTimer(); +#if HAVE_DECL_SO_SNDBUF + int socket = mapSocket->GetSocketHandle(); + int newWindow = 1<<17; + if(setsockopt(socket, SOL_SOCKET, SO_SNDBUF, +# ifdef WIN32 + // optval is a const char * on Windows, even + // though the argument is a boolean or integer, + // for reasons best known to Microsoft! + (const char *)&newWindow, +# else + &newWindow, +# endif + sizeof(newWindow)) == -1) + { + BOX_LOG_SYS_WARNING("getsockopt(" << socket << ", SOL_SOCKET, " + "SO_SNDBUF, " << newWindow << ") failed"); + } +#endif + } +} diff --git a/lib/server/TcpNice.h b/lib/server/TcpNice.h new file mode 100644 index 00000000..e2027749 --- /dev/null +++ b/lib/server/TcpNice.h @@ -0,0 +1,174 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: TcpNice.h +// Purpose: Calculator for adaptive TCP window sizing to support +// low-priority background flows using the stochastic +// algorithm, as described in +// http://www.thlab.net/~lmassoul/p18-key.pdf +// Created: 11/02/2012 +// +// -------------------------------------------------------------------------- + +#ifndef TCPNICE__H +#define TCPNICE__H + +#include <memory> + +#include "SocketStream.h" +#include "Timer.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: TcpNice +// Purpose: Calculator for adaptive TCP window sizing. +// Created: 11/02/2012 +// +// -------------------------------------------------------------------------- + +class TcpNice +{ +public: + TcpNice(); + int GetNextWindowSize(int bytesChange, box_time_t timeElapsed, + int rttEstimateMicros); + +private: + /** + * The previous (last recommended) window size is one of the parameters + * used to calculate the next window size. + */ + int mLastWindowSize; + + /** + * Controls the speed of adaptation and the variance (random variation) + * of the stable state in response to noise. The paper suggests using + * 1.0 (100%). + */ + int mGammaPercent; + + /** + * Controls the extent to which background flows are allowed to affect + * foreground flows. Its detailed meaning is not explained in the paper, + * but its units are bytes, and I think it controls how aggressive we + * are at increasing window size, potentially at the expense of other + * competing flows. + */ + int mAlphaStar; + + /** + * Controls the speed of adaptation of the exponential weighted moving + * average (EWMA) estimate of the bandwidth available to this flow. + * The paper uses 10%. + */ + int mDeltaPercent; + + /** + * The stochastic algorithm in the paper uses the rate estimate for the + * last-but-one period (rbHat[n-2]) to calculate the next window size. + * So we keep both the last (in rateEstimateMovingAverage[1]) and the + * last-but-one (in rateEstimateMovingAverage[0]) values. + */ + int mRateEstimateMovingAverage[2]; +}; + +// -------------------------------------------------------------------------- +// +// Class +// Name: NiceSocketStream +// Purpose: Wrapper around a SocketStream to limit sending rate to +// avoid interference with higher-priority flows. +// Created: 11/02/2012 +// +// -------------------------------------------------------------------------- + +class NiceSocketStream : public IOStream +{ +private: + std::auto_ptr<SocketStream> mapSocket; + TcpNice mTcpNice; + std::auto_ptr<Timer> mapTimer; + int mBytesWrittenThisPeriod; + box_time_t mPeriodStartTime; + + /** + * The control interval T from the paper, in milliseconds. The available + * bandwidth is estimated over this period, and the window size is + * recalculated at the end of each period. It should be long enough for + * TCP to adapt to a change in window size; perhaps 10-100 RTTs. One + * second (1000) is probably a good first approximation in many cases. + */ + int mTimeIntervalMillis; + + /** + * Because our data use is bursty, and tcp nice works on the assumption + * that we've always got data to send, we should only enable nice mode + * when we're doing a bulk upload, and disable it afterwards. + */ + bool mEnabled; + + void StartTimer() + { + mapTimer.reset(new Timer(mTimeIntervalMillis, "NiceSocketStream")); + } + + void StopTimer() + { + mapTimer.reset(); + } + +public: + NiceSocketStream(std::auto_ptr<SocketStream> apSocket); + virtual ~NiceSocketStream() + { + // Be nice about closing the socket + mapSocket->Shutdown(); + mapSocket->Close(); + } + + // This is the only magic + virtual void Write(const void *pBuffer, int NBytes); + + // Everything else is delegated to the sink + virtual int Read(void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite) + { + return mapSocket->Read(pBuffer, NBytes, Timeout); + } + virtual pos_type BytesLeftToRead() + { + return mapSocket->BytesLeftToRead(); + } + virtual pos_type GetPosition() const + { + return mapSocket->GetPosition(); + } + virtual void Seek(IOStream::pos_type Offset, int SeekType) + { + mapSocket->Seek(Offset, SeekType); + } + virtual void Flush(int Timeout = IOStream::TimeOutInfinite) + { + mapSocket->Flush(Timeout); + } + virtual void Close() + { + mapSocket->Close(); + } + virtual bool StreamDataLeft() + { + return mapSocket->StreamDataLeft(); + } + virtual bool StreamClosed() + { + return mapSocket->StreamClosed(); + } + virtual void SetEnabled(bool enabled); + +private: + NiceSocketStream(const NiceSocketStream &rToCopy) + { /* do not call */ } +}; + +#endif // TCPNICE__H diff --git a/lib/server/makeprotocol.pl.in b/lib/server/makeprotocol.pl.in index 91ba55b0..9caa970d 100755 --- a/lib/server/makeprotocol.pl.in +++ b/lib/server/makeprotocol.pl.in @@ -30,6 +30,7 @@ my %log_display_types = 'string' => ['%s', 'VAR.c_str()'] ); +<<<<<<< HEAD my ($type, $file) = @ARGV; @@ -48,6 +49,21 @@ my @extra_header_files; my $implement_syslog = 0; my $implement_filelog = 0; +======= +if (@ARGV != 1) +{ + die "Usage: $0 <protocol-txt-file>\n"; +} + +my ($file) = @ARGV; + +open IN, $file or die "Can't open input file $file\n"; + +print "Making protocol classes from $file...\n"; + +my @extra_header_files; + +>>>>>>> 0.12 # read attributes my %attr; while(<IN>) @@ -59,6 +75,7 @@ while(<IN>) my ($k,$v) = split /\s+/,$l,2; +<<<<<<< HEAD if($k eq 'ClientType') { add_type($v) if $type eq 'Client'; @@ -94,6 +111,20 @@ while(<IN>) { $log_display_types{$type_name} = [$printf_format,$arg_template] } +======= + if($k eq 'AddType') + { + add_type($v); + } + elsif($k eq 'ImplementLog') + { + # Always implement logging + } + elsif($k eq 'LogTypeToText') + { + my ($type_name,$printf_format,$arg_template) = split /\s+/,$v; + $log_display_types{$type_name} = [$printf_format,$arg_template] +>>>>>>> 0.12 } else { @@ -169,9 +200,19 @@ close IN; # open files +<<<<<<< HEAD my $h_filename = 'autogen_'.$protocol_name.'Protocol'.$type.'.h'; open CPP,'>autogen_'.$protocol_name.'Protocol'.$type.'.cpp'; open H,">$h_filename"; +======= +my $filename_base = 'autogen_'.$protocol_name.'Protocol'; +print "Writing $filename_base.cpp\n"; +print "Writing $filename_base.h\n"; +open CPP, "> $filename_base.cpp"; +open H, "> $filename_base.h"; + +my $guardname = uc 'AUTOGEN_'.$protocol_name.'Protocol_H'; +>>>>>>> 0.12 print CPP <<__E; @@ -181,6 +222,7 @@ print CPP <<__E; #include <sstream> +<<<<<<< HEAD #include "$h_filename" #include "IOStream.h" @@ -199,27 +241,52 @@ EOF my $guardname = uc 'AUTOGEN_'.$protocol_name.'Protocol'.$type.'_H'; print H <<__E; +======= +#include "$filename_base.h" +#include "IOStream.h" +__E + +print H <<__E; +>>>>>>> 0.12 // Auto-generated file -- do not edit #ifndef $guardname #define $guardname +<<<<<<< HEAD #include "Protocol.h" #include "ProtocolObject.h" +======= +#include <cstdio> +#include <list> + +#ifndef WIN32 +#include <syslog.h> +#endif + +#include "Protocol.h" +#include "Message.h" +>>>>>>> 0.12 #include "ServerException.h" class IOStream; +<<<<<<< HEAD __E if($implement_filelog) { print H qq~#include <stdio.h>\n~; } +======= + +__E +>>>>>>> 0.12 # extra headers for(@extra_header_files) { +<<<<<<< HEAD print H qq~#include "$_"\n~ } print H "\n"; @@ -320,12 +387,103 @@ public: $classname_base$cmd(); $classname_base$cmd(const $classname_base$cmd &rToCopy); ~$classname_base$cmd(); +======= + print H qq@#include "$_"\n@; +} + +print H <<__E; + +// need utils file for the server +#include "Utils.h" + +__E + +my $message_base_class = "${protocol_name}ProtocolMessage"; +my $objects_extra_h = ''; +my $objects_extra_cpp = ''; + +# define the context +print H "class $context_class;\n\n"; +print CPP <<__E; +#include "$context_class_inc" +#include "MemLeakFindOn.h" +__E + +my $request_base_class = "${protocol_name}ProtocolRequest"; +my $reply_base_class = "${protocol_name}ProtocolReply"; +# the abstract protocol interface +my $protocol_base_class = $protocol_name."ProtocolBase"; +my $replyable_base_class = $protocol_name."ProtocolReplyable"; + +print H <<__E; +class $protocol_base_class; +class $replyable_base_class; +class $reply_base_class; + +class $message_base_class : public Message +{ +public: + virtual std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, + $context_class &rContext) const; +}; + +class $reply_base_class +{ +}; + +class $request_base_class +{ +}; + +__E + +print CPP <<__E; +std::auto_ptr<$message_base_class> $message_base_class\::DoCommand($replyable_base_class &rProtocol, + $context_class &rContext) const +{ + THROW_EXCEPTION(ConnectionException, Conn_Protocol_TriedToExecuteReplyCommand) +} +__E + +my %cmd_class; + +# output the classes +foreach my $cmd (@cmd_list) +{ + my @cmd_base_classes = ($message_base_class); + + if(obj_is_type($cmd, 'Command')) + { + push @cmd_base_classes, $request_base_class; + } + + if(obj_is_type($cmd, 'Reply')) + { + push @cmd_base_classes, $reply_base_class; + } + + my $cmd_base_class = join(", ", map {"public $_"} @cmd_base_classes); + my $cmd_class = $protocol_name."Protocol".$cmd; + $cmd_class{$cmd} = $cmd_class; + + print H <<__E; +class $cmd_class : $cmd_base_class +{ +public: + $cmd_class(); + $cmd_class(const $cmd_class &rToCopy); + ~$cmd_class(); +>>>>>>> 0.12 int GetType() const; enum { TypeID = $cmd_id{$cmd} }; __E +<<<<<<< HEAD +======= + +>>>>>>> 0.12 # constants if(exists $cmd_constants{$cmd}) { @@ -333,16 +491,25 @@ __E print H join(",\n\t\t",@{$cmd_constants{$cmd}}); print H "\n\t};\n"; } +<<<<<<< HEAD +======= + +>>>>>>> 0.12 # flags if(obj_is_type($cmd,'EndsConversation')) { print H "\tbool IsConversationEnd() const;\n"; } +<<<<<<< HEAD +======= + +>>>>>>> 0.12 if(obj_is_type($cmd,'IsError')) { print H "\tbool IsError(int &rTypeOut, int &rSubTypeOut) const;\n"; print H "\tstd::string GetMessage() const;\n"; } +<<<<<<< HEAD if($type eq 'Server' && obj_is_type($cmd, 'Command')) { print H "\tstd::auto_ptr<ProtocolObject> DoCommand(${protocol_name}ProtocolServer &rProtocol, $context_class &rContext); // IMPLEMENT THIS\n" @@ -399,6 +566,54 @@ __E print H "\tvirtual void LogFile(const char *Action, FILE *file) const;\n"; } +======= + + if(obj_is_type($cmd, 'Command')) + { + print H <<__E; + std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, + $context_class &rContext) const; // IMPLEMENT THIS\n +__E + } + + # want to be able to read from streams? + print H "\tvoid SetPropertiesFromStreamData(Protocol &rProtocol);\n"; + + # write Get functions + for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) + { + my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); + + print H "\t".translate_type_to_arg_type($ty)." Get$nm() {return m$nm;}\n"; + } + + my $param_con_args = ''; + # extra constructor? + if($#{$cmd_contents{$cmd}} >= 0) + { + my @a; + for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) + { + my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); + + push @a,translate_type_to_arg_type($ty)." $nm"; + } + $param_con_args = join(', ',@a); + print H "\t$cmd_class(".$param_con_args.");\n"; + } + print H "\tvoid WritePropertiesToStreamData(Protocol &rProtocol) const;\n"; + # set functions + for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) + { + my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); + + print H "\tvoid Set$nm(".translate_type_to_arg_type($ty)." $nm) {m$nm = $nm;}\n"; + } + + print H "\tvirtual void LogSysLog(const char *Action) const;\n"; + print H "\tvirtual void LogFile(const char *Action, FILE *file) const;\n"; + print H "\tvirtual std::string ToString() const;\n"; +>>>>>>> 0.12 # write member variables and setup for cpp file my @def_constructor_list; @@ -432,6 +647,7 @@ __E my $param_con_vars = join(",\n\t ",@param_constructor_list); $param_con_vars = "\n\t: ".$param_con_vars if $param_con_vars ne ''; +<<<<<<< HEAD my $class = "$classname_base$cmd".'::'; print CPP <<__E; $class$classname_base$cmd()$def_con_vars @@ -444,10 +660,24 @@ $class~$classname_base$cmd() { } int ${class}GetType() const +======= + print CPP <<__E; +$cmd_class\::$cmd_class()$def_con_vars +{ +} +$cmd_class\::$cmd_class(const $cmd_class &rToCopy)$copy_con_vars +{ +} +$cmd_class\::~$cmd_class() +{ +} +int $cmd_class\::GetType() const +>>>>>>> 0.12 { return $cmd_id{$cmd}; } __E +<<<<<<< HEAD if($read_from_streams) { print CPP "void ${class}SetPropertiesFromStreamData(Protocol &rProtocol)\n{\n"; @@ -491,18 +721,68 @@ __E { print CPP "bool ${class}IsConversationEnd() const\n{\n\treturn true;\n}\n"; } +======= + print CPP "void $cmd_class\::SetPropertiesFromStreamData(Protocol &rProtocol)\n{\n"; + for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) + { + my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); + if($ty =~ m/\Avector/) + { + print CPP "\trProtocol.ReadVector(m$nm);\n"; + } + else + { + print CPP "\trProtocol.Read(m$nm);\n"; + } + } + print CPP "}\n"; + + # implement extra constructor? + if($param_con_vars ne '') + { + print CPP "$cmd_class\::$cmd_class($param_con_args)$param_con_vars\n{\n}\n"; + } + print CPP "void $cmd_class\::WritePropertiesToStreamData(Protocol &rProtocol) const\n{\n"; + for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) + { + my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); + if($ty =~ m/\Avector/) + { + print CPP "\trProtocol.WriteVector(m$nm);\n"; + } + else + { + print CPP "\trProtocol.Write(m$nm);\n"; + } + } + print CPP "}\n"; + + if(obj_is_type($cmd,'EndsConversation')) + { + print CPP "bool $cmd_class\::IsConversationEnd() const\n{\n\treturn true;\n}\n"; + } + +>>>>>>> 0.12 if(obj_is_type($cmd,'IsError')) { # get parameters my ($mem_type,$mem_subtype) = split /,/,obj_get_type_params($cmd,'IsError'); print CPP <<__E; +<<<<<<< HEAD bool ${class}IsError(int &rTypeOut, int &rSubTypeOut) const +======= +bool $cmd_class\::IsError(int &rTypeOut, int &rSubTypeOut) const +>>>>>>> 0.12 { rTypeOut = m$mem_type; rSubTypeOut = m$mem_subtype; return true; } +<<<<<<< HEAD std::string ${class}GetMessage() const +======= +std::string $cmd_class\::GetMessage() const +>>>>>>> 0.12 { switch(m$mem_subtype) { @@ -526,6 +806,7 @@ __E __E } +<<<<<<< HEAD if($implement_syslog) { my ($log) = make_log_strings_framework($cmd); @@ -667,11 +948,453 @@ const char *${prefix}GetIdentString() return "$ident_string"; } std::auto_ptr<ProtocolObject> ${prefix}MakeProtocolObject(int ObjType) +======= + my ($log) = make_log_strings_framework($cmd); + print CPP <<__E; +std::string $cmd_class\::ToString() const +{ + std::ostringstream oss; + try + { + oss << $log; + } + catch(std::exception &e) + { + oss << "Failed to log command: " << e.what(); + } + return oss.str(); +} +void $cmd_class\::LogSysLog(const char *Action) const +{ + try + { + BOX_TRACE(Action << " " << $log); + } + catch(std::exception &e) + { + BOX_WARNING("Failed to log command: " << Action << ": " << + e.what()); + } +} +void $cmd_class\::LogFile(const char *Action, FILE *File) const +{ + ::fprintf(File, "%s %s\\n", Action, ToString().c_str()); + ::fflush(File); +} +__E +} + +my $error_class = $protocol_name."ProtocolError"; + +# the abstract protocol interface +print H <<__E; +class $protocol_base_class +{ +public: + $protocol_base_class(); + virtual ~$protocol_base_class(); + virtual const char *GetIdentString(); + bool GetLastError(int &rTypeOut, int &rSubTypeOut); + +protected: + void CheckReply(const std::string& requestCommand, + const $message_base_class &rReply, int expectedType); + void SetLastError(int Type, int SubType) + { + mLastErrorType = Type; + mLastErrorSubType = SubType; + } + +private: + $protocol_base_class(const $protocol_base_class &rToCopy); /* do not call */ + int mLastErrorType; + int mLastErrorSubType; +}; + +class $replyable_base_class : public virtual $protocol_base_class +{ +public: + $replyable_base_class(); + virtual ~$replyable_base_class(); + + /* + virtual std::auto_ptr<$message_base_class> Receive() = 0; + virtual void Send(const ${message_base_class} &rObject) = 0; + */ + + virtual std::auto_ptr<IOStream> ReceiveStream() = 0; + virtual int GetTimeout() = 0; + void SendStreamAfterCommand(std::auto_ptr<IOStream> apStream); + +protected: + std::list<IOStream*> mStreamsToSend; + void DeleteStreamsToSend(); + +private: + $replyable_base_class(const $replyable_base_class &rToCopy); /* do not call */ +}; + +__E + +print CPP <<__E; +$protocol_base_class\::$protocol_base_class() +: mLastErrorType(Protocol::NoError), + mLastErrorSubType(Protocol::NoError) +{ } + +$protocol_base_class\::~$protocol_base_class() +{ } + +const char *$protocol_base_class\::GetIdentString() +{ + return "$ident_string"; +} + +$replyable_base_class\::$replyable_base_class() +{ } + +$replyable_base_class\::~$replyable_base_class() +{ } + +void $replyable_base_class\::SendStreamAfterCommand(std::auto_ptr<IOStream> apStream) +{ + ASSERT(apStream.get() != NULL); + mStreamsToSend.push_back(apStream.release()); +} + +void $replyable_base_class\::DeleteStreamsToSend() +{ + for(std::list<IOStream*>::iterator i(mStreamsToSend.begin()); i != mStreamsToSend.end(); ++i) + { + delete (*i); + } + + mStreamsToSend.clear(); +} + +void $protocol_base_class\::CheckReply(const std::string& requestCommand, + const $message_base_class &rReply, int expectedType) +{ + if(rReply.GetType() == expectedType) + { + // Correct response, do nothing + } + else + { + // Set protocol error + int type, subType; + + if(rReply.IsError(type, subType)) + { + SetLastError(type, subType); + THROW_EXCEPTION_MESSAGE(ConnectionException, + Conn_Protocol_UnexpectedReply, + requestCommand << " command failed: " + "received error " << + (($error_class&)rReply).GetMessage()); + } + else + { + SetLastError(Protocol::UnknownError, Protocol::UnknownError); + THROW_EXCEPTION_MESSAGE(ConnectionException, + Conn_Protocol_UnexpectedReply, + requestCommand << " command failed: " + "received unexpected response type " << + rReply.GetType()); + } + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Protocol::GetLastError(int &, int &) +// Purpose: Returns true if there was an error, and type and subtype if there was. +// Created: 2003/08/19 +// +// -------------------------------------------------------------------------- +bool $protocol_base_class\::GetLastError(int &rTypeOut, int &rSubTypeOut) +{ + if(mLastErrorType == Protocol::NoError) + { + // no error. + return false; + } + + // Return type and subtype in args + rTypeOut = mLastErrorType; + rSubTypeOut = mLastErrorSubType; + + // and unset them + mLastErrorType = Protocol::NoError; + mLastErrorSubType = Protocol::NoError; + + return true; +} + +__E + +# the callable protocol interface (implemented by Client and Local classes) +# with Query methods that don't take a context parameter +my $callable_base_class = $protocol_name."ProtocolCallable"; +print H <<__E; +class $callable_base_class : public virtual $protocol_base_class +{ +public: + virtual std::auto_ptr<IOStream> ReceiveStream() = 0; + virtual int GetTimeout() = 0; +__E + +# add plain object taking query functions +my $with_params; +for my $cmd (@cmd_list) +{ + if(obj_is_type($cmd,'Command')) + { + my $has_stream = obj_is_type($cmd,'StreamWithCommand'); + my $argextra = $has_stream?', std::auto_ptr<IOStream> apStream':''; + my $queryextra = $has_stream?', apStream':''; + my $request_class = $cmd_class{$cmd}; + my $reply_class = $cmd_class{obj_get_type_params($cmd,'Command')}; + + print H "\tvirtual std::auto_ptr<$reply_class> Query(const $request_class &rQuery$argextra) = 0;\n"; + my @a; + my @na; + for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) + { + my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); + push @a,translate_type_to_arg_type($ty)." $nm"; + push @na,"$nm"; + } + my $ar = join(', ',@a); + my $nar = join(', ',@na); + $nar = "($nar)" if $nar ne ''; + + $with_params .= <<__E; + inline std::auto_ptr<$reply_class> Query$cmd($ar$argextra) + { + $request_class send$nar; + return Query(send$queryextra); + } +__E + } +} + +# quick hack to correct bad argument lists for commands with zero parameters but with streams +$with_params =~ s/\(, /(/g; + +print H <<__E; + +$with_params +}; +__E + +# standard remote protocol objects +foreach my $type ('Client', 'Server', 'Local') +{ + my $writing_client = ($type eq 'Client'); + my $writing_server = ($type eq 'Server'); + my $writing_local = ($type eq 'Local'); + + my $server_or_client_class = $protocol_name."Protocol".$type; + my @base_classes; + + if (not $writing_client) + { + push @base_classes, $replyable_base_class; + } + if (not $writing_server) + { + push @base_classes, $callable_base_class; + } + if (not $writing_local) + { + push @base_classes, "Protocol"; + } + + my $base_classes_str = join(", ", map {"public $_"} @base_classes); + + print H <<__E; +class $server_or_client_class : $base_classes_str +{ +public: +__E + + if($writing_local) + { + print H <<__E; + $server_or_client_class($context_class &rContext); +__E + } + else + { + print H <<__E; + $server_or_client_class(IOStream &rStream); + std::auto_ptr<$message_base_class> Receive(); + void Send(const $message_base_class &rObject); +__E + } + + print H <<__E; + virtual ~$server_or_client_class(); +__E + + if($writing_server) + { + # need to put in the conversation function + print H <<__E; + void DoServer($context_class &rContext); + +__E + } + + if($writing_client or $writing_local) + { + # add plain object taking query functions + for my $cmd (@cmd_list) + { + if(obj_is_type($cmd,'Command')) + { + my $has_stream = obj_is_type($cmd,'StreamWithCommand'); + my $argextra = $has_stream?', std::auto_ptr<IOStream> apStream':''; + my $queryextra = $has_stream?', apStream':''; + my $request_class = $cmd_class{$cmd}; + my $reply_class = $cmd_class{obj_get_type_params($cmd,'Command')}; + print H "\tstd::auto_ptr<$reply_class> Query(const $request_class &rQuery$argextra);\n"; + } + } + } + + if($writing_local) + { + print H <<__E; +private: + $context_class &mrContext; +__E + } + + print H <<__E; + +protected: + virtual std::auto_ptr<Message> MakeMessage(int ObjType); + +__E + + if($writing_local) + { + print H <<__E; + virtual void InformStreamReceiving(u_int32_t Size) { } + virtual void InformStreamSending(u_int32_t Size) { } + +public: + virtual std::auto_ptr<IOStream> ReceiveStream() + { + std::auto_ptr<IOStream> apStream(mStreamsToSend.front()); + mStreamsToSend.pop_front(); + return apStream; + } +__E + } + else + { + print H <<__E; + virtual void InformStreamReceiving(u_int32_t Size) + { + this->Protocol::InformStreamReceiving(Size); + } + virtual void InformStreamSending(u_int32_t Size) + { + this->Protocol::InformStreamSending(Size); + } + +public: + virtual std::auto_ptr<IOStream> ReceiveStream() + { + return this->Protocol::ReceiveStream(); + } +__E + } + + print H <<__E; + virtual const char *GetProtocolIdentString() + { + return GetIdentString(); + } +__E + + if($writing_local) + { + print H <<__E; + virtual int GetTimeout() + { + return IOStream::TimeOutInfinite; + } +__E + } + else + { + print H <<__E; + virtual int GetTimeout() + { + return this->Protocol::GetTimeout(); + } +__E + } + + print H <<__E; + /* + virtual void Handshake() + { + this->Protocol::Handshake(); + } + virtual bool GetLastError(int &rTypeOut, int &rSubTypeOut) + { + return this->Protocol::GetLastError(rTypeOut, rSubTypeOut); + } + */ + +private: + $server_or_client_class(const $server_or_client_class &rToCopy); /* no copies */ +}; + +__E + + my $destructor_extra = ($writing_server) ? "\n\tDeleteStreamsToSend();" + : ''; + + if($writing_local) + { + print CPP <<__E; +$server_or_client_class\::$server_or_client_class($context_class &rContext) +: mrContext(rContext) +{ } +__E + } + else + { + print CPP <<__E; +$server_or_client_class\::$server_or_client_class(IOStream &rStream) +: Protocol(rStream) +{ } +__E + } + + print CPP <<__E; +$server_or_client_class\::~$server_or_client_class() +{$destructor_extra +} +__E + + # write receive and send functions + print CPP <<__E; +std::auto_ptr<Message> $server_or_client_class\::MakeMessage(int ObjType) +>>>>>>> 0.12 { switch(ObjType) { __E +<<<<<<< HEAD # do objects within this for my $cmd (@cmd_list) { @@ -683,11 +1406,25 @@ __E } print CPP <<__E; +======= + # do objects within this + for my $cmd (@cmd_list) + { + print CPP <<__E; + case $cmd_id{$cmd}: + return std::auto_ptr<Message>(new $cmd_class{$cmd}()); + break; +__E + } + + print CPP <<__E; +>>>>>>> 0.12 default: THROW_EXCEPTION(ConnectionException, Conn_Protocol_UnknownCommandRecieved) } } __E +<<<<<<< HEAD # write receive and send functions print CPP <<__E; std::auto_ptr<$derive_objects_from> ${prefix}Receive() @@ -714,10 +1451,31 @@ __E __E } print CPP <<__E; +======= + + if(not $writing_local) + { + print CPP <<__E; +std::auto_ptr<$message_base_class> $server_or_client_class\::Receive() +{ + std::auto_ptr<$message_base_class> preply(($message_base_class *) + Protocol::ReceiveInternal().release()); + + if(GetLogToSysLog()) + { + preply->LogSysLog("Receive"); + } + + if(GetLogToFile() != 0) + { + preply->LogFile("Receive", GetLogToFile()); + } +>>>>>>> 0.12 return preply; } +<<<<<<< HEAD void ${prefix}Send(const ${derive_objects_from} &rObject) { __E @@ -750,6 +1508,31 @@ if($type eq 'Server') { print CPP <<__E; void ${prefix}DoServer($context_class &rContext) +======= +void $server_or_client_class\::Send(const $message_base_class &rObject) +{ + if(GetLogToSysLog()) + { + rObject.LogSysLog("Send"); + } + + if(GetLogToFile() != 0) + { + rObject.LogFile("Send", GetLogToFile()); + } + + Protocol::SendInternal(rObject); +} + +__E + } + + # write server function? + if($writing_server) + { + print CPP <<__E; +void $server_or_client_class\::DoServer($context_class &rContext) +>>>>>>> 0.12 { // Handshake with client Handshake(); @@ -759,6 +1542,7 @@ void ${prefix}DoServer($context_class &rContext) while(inProgress) { // Get an object from the conversation +<<<<<<< HEAD std::auto_ptr<${derive_objects_from}> pobj(Receive()); // Run the command @@ -773,6 +1557,24 @@ void ${prefix}DoServer($context_class &rContext) // Send the streams SendStream(*mStreamsToSend[s]); } +======= + std::auto_ptr<$message_base_class> pobj = Receive(); + + // Run the command + std::auto_ptr<$message_base_class> preply = pobj->DoCommand(*this, rContext); + + // Send the reply + Send(*preply); + + // Send any streams + for(std::list<IOStream*>::iterator + i = mStreamsToSend.begin(); + i != mStreamsToSend.end(); ++i) + { + SendStream(**i); + } + +>>>>>>> 0.12 // Delete these streams DeleteStreamsToSend(); @@ -784,6 +1586,7 @@ void ${prefix}DoServer($context_class &rContext) } } +<<<<<<< HEAD void ${prefix}SendStreamAfterCommand(IOStream *pStream) { ASSERT(pStream != NULL); @@ -898,10 +1701,45 @@ __E } print CPP <<__E; std::auto_ptr<$classname_base$reply> ${classname_base}::Query(const $classname_base$cmd &rQuery$argextra) +======= +__E + } + + # write client Query functions? + if($writing_client or $writing_local) + { + for my $cmd (@cmd_list) + { + if(obj_is_type($cmd,'Command')) + { + my $request_class = $cmd_class{$cmd}; + my $reply_msg = obj_get_type_params($cmd,'Command'); + my $reply_class = $cmd_class{$reply_msg}; + my $reply_id = $cmd_id{$reply_msg}; + my $has_stream = obj_is_type($cmd,'StreamWithCommand'); + my $argextra = $has_stream?', std::auto_ptr<IOStream> apStream':''; + my $send_stream_extra = ''; + my $send_stream_method = $writing_client ? "SendStream" + : "SendStreamAfterCommand"; + + if($writing_client) + { + if($has_stream) + { + $send_stream_extra = <<__E; + // Send stream after the command + SendStream(*apStream); +__E + } + + print CPP <<__E; +std::auto_ptr<$reply_class> $server_or_client_class\::Query(const $request_class &rQuery$argextra) +>>>>>>> 0.12 { // Send query Send(rQuery); $send_stream_extra +<<<<<<< HEAD // Wait for the reply std::auto_ptr<${derive_objects_from}> preply(Receive().release()); @@ -933,12 +1771,53 @@ std::auto_ptr<$classname_base$reply> ${classname_base}::Query(const $classname_b } } __E +======= + + // Wait for the reply + std::auto_ptr<$message_base_class> preply = Receive(); + + CheckReply("$cmd", *preply, $reply_id); + + // Correct response, if no exception thrown by CheckReply + return std::auto_ptr<$reply_class>(($reply_class *)preply.release()); +} +__E + } + elsif($writing_local) + { + if($has_stream) + { + $send_stream_extra = <<__E; + // Send stream after the command + SendStreamAfterCommand(apStream); +__E + } + + print CPP <<__E; +std::auto_ptr<$reply_class> $server_or_client_class\::Query(const $request_class &rQuery$argextra) +{ + // Send query + $send_stream_extra + std::auto_ptr<$message_base_class> preply = rQuery.DoCommand(*this, mrContext); + + CheckReply("$cmd", *preply, $reply_id); + + // Correct response, if no exception thrown by CheckReply + return std::auto_ptr<$reply_class>(($reply_class *)preply.release()); +} +__E + } + } +>>>>>>> 0.12 } } } +<<<<<<< HEAD +======= +>>>>>>> 0.12 print H <<__E; #endif // $guardname @@ -948,8 +1827,12 @@ __E close H; close CPP; +<<<<<<< HEAD sub obj_is_type +======= +sub obj_is_type ($$) +>>>>>>> 0.12 { my ($c,$ty) = @_; for(@{$cmd_attributes{$c}}) @@ -1003,6 +1886,7 @@ sub translate_type_to_member_type return $typename } +<<<<<<< HEAD sub make_log_strings { my ($cmd) = @_; @@ -1037,6 +1921,8 @@ sub make_log_strings return ($cmd.'('.join(',',@str).')', join(',','',@arg)); } +======= +>>>>>>> 0.12 sub make_log_strings_framework { my ($cmd) = @_; @@ -1053,7 +1939,11 @@ sub make_log_strings_framework my ($format,$arg) = @{$log_display_types{$ty}}; $arg =~ s/VAR/m$nm/g; +<<<<<<< HEAD if ($format eq '\\"%s\\"') +======= + if ($format eq '"%s"') +>>>>>>> 0.12 { $arg = "\"\\\"\" << $arg << \"\\\"\""; } @@ -1080,7 +1970,11 @@ sub make_log_strings_framework } } +<<<<<<< HEAD my $log_cmd = "Action << \" $cmd(\" "; +======= + my $log_cmd = '"'.$cmd.'(" '; +>>>>>>> 0.12 foreach my $arg (@args) { $arg = "<< $arg "; @@ -1090,4 +1984,7 @@ sub make_log_strings_framework return $log_cmd; } +<<<<<<< HEAD +======= +>>>>>>> 0.12 |