summaryrefslogtreecommitdiff
path: root/lib/server
diff options
context:
space:
mode:
Diffstat (limited to 'lib/server')
-rw-r--r--lib/server/ConnectionException.txt2
-rw-r--r--lib/server/Daemon.cpp450
-rw-r--r--lib/server/Daemon.h18
-rw-r--r--lib/server/LocalProcessStream.cpp82
-rw-r--r--lib/server/LocalProcessStream.h2
-rw-r--r--lib/server/Makefile.extra4
-rw-r--r--lib/server/Protocol.cpp6
-rw-r--r--lib/server/Protocol.h2
-rw-r--r--lib/server/ProtocolObject.cpp2
-rw-r--r--lib/server/ProtocolObject.h2
-rw-r--r--lib/server/ProtocolUncertainStream.cpp2
-rw-r--r--lib/server/ProtocolUncertainStream.h6
-rw-r--r--lib/server/ProtocolWire.h2
-rw-r--r--lib/server/SSLLib.cpp18
-rw-r--r--lib/server/SSLLib.h2
-rw-r--r--lib/server/ServerControl.h228
-rw-r--r--lib/server/ServerException.h2
-rw-r--r--lib/server/ServerStream.h87
-rw-r--r--lib/server/ServerTLS.h2
-rw-r--r--lib/server/Socket.cpp11
-rw-r--r--lib/server/Socket.h2
-rw-r--r--lib/server/SocketListen.h2
-rw-r--r--lib/server/SocketStream.cpp66
-rw-r--r--lib/server/SocketStream.h5
-rw-r--r--lib/server/SocketStreamTLS.cpp43
-rw-r--r--lib/server/SocketStreamTLS.h2
-rw-r--r--lib/server/TLSContext.cpp7
-rw-r--r--lib/server/TLSContext.h2
-rw-r--r--lib/server/WinNamedPipeStream.cpp629
-rw-r--r--lib/server/WinNamedPipeStream.h106
-rwxr-xr-xlib/server/makeprotocol.pl63
-rwxr-xr-xlib/server/makeprotocol.pl.in1048
32 files changed, 2675 insertions, 230 deletions
diff --git a/lib/server/ConnectionException.txt b/lib/server/ConnectionException.txt
index 5056754f..c3429116 100644
--- a/lib/server/ConnectionException.txt
+++ b/lib/server/ConnectionException.txt
@@ -10,7 +10,7 @@ SocketConnectError 15 Probably a network issue between client and server, bad
TLSHandshakeFailed 30
TLSShutdownFailed 32
TLSWriteFailed 33 Probably a network issue between client and server.
-TLSReadFailed 34 Probably a network issue between client and server.
+TLSReadFailed 34 Probably a network issue between client and server, or a problem with the server.
TLSNoPeerCertificate 36
TLSPeerCertificateInvalid 37 Check certification process
TLSClosedWhenWriting 38
diff --git a/lib/server/Daemon.cpp b/lib/server/Daemon.cpp
index 3366383a..facac900 100644
--- a/lib/server/Daemon.cpp
+++ b/lib/server/Daemon.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -57,16 +57,19 @@
#include <string.h>
#include <stdarg.h>
-#ifdef HAVE_SYSLOG_H
- #include <syslog.h>
+#ifdef WIN32
+ #include <ws2tcpip.h>
#endif
+#include <iostream>
+
#include "Daemon.h"
#include "Configuration.h"
#include "ServerException.h"
#include "Guards.h"
#include "UnixUser.h"
#include "FileModificationTime.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -82,11 +85,16 @@ Daemon *Daemon::spDaemon = 0;
//
// --------------------------------------------------------------------------
Daemon::Daemon()
- : mpConfiguration(0),
+ : mpConfiguration(NULL),
mReloadConfigWanted(false),
- mTerminateWanted(false)
+ mTerminateWanted(false),
+ mSingleProcess(false),
+ mRunInForeground(false),
+ mKeepConsoleOpenAfterFork(false),
+ mHaveConfigFile(false),
+ mAppName(DaemonName())
{
- if(spDaemon != 0)
+ if(spDaemon != NULL)
{
THROW_EXCEPTION(ServerException, AlreadyDaemonConstructed)
}
@@ -113,56 +121,267 @@ Daemon::~Daemon()
delete mpConfiguration;
mpConfiguration = 0;
}
+
+ ASSERT(spDaemon == this);
+ spDaemon = NULL;
}
// --------------------------------------------------------------------------
//
// Function
-// Name: Daemon::Main(const char *, int, const char *[])
-// Purpose: Starts the daemon off -- equivalent of C main() function
-// Created: 2003/07/29
+// Name: Daemon::GetOptionString()
+// Purpose: Returns the valid Getopt command-line options.
+// This should be overridden by subclasses to add
+// their own options, which should override
+// ProcessOption, handle their own, and delegate to
+// ProcessOption for the standard options.
+// Created: 2007/09/18
//
// --------------------------------------------------------------------------
-int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
+std::string Daemon::GetOptionString()
{
- // Banner (optional)
+ return "c:"
+ #ifndef WIN32
+ "DFk"
+ #endif
+ "hqvVt:T";
+}
+
+void Daemon::Usage()
+{
+ std::cout <<
+ DaemonBanner() << "\n"
+ "\n"
+ "Usage: " << mAppName << " [options] [config file]\n"
+ "\n"
+ "Options:\n"
+ " -c <file> Use the specified configuration file. If -c is omitted, the last\n"
+ " argument is the configuration file, or else the default \n"
+ " [" << GetConfigFileName() << "]\n"
+#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"
+ " -k Keep console open after fork, keep writing log messages to it\n"
+#endif
+ " -q Run more quietly, reduce verbosity level by one, can repeat\n"
+ " -v Run more verbosely, increase verbosity level by one, can repeat\n"
+ " -V Run at maximum verbosity\n"
+ " -t <tag> Tag console output with specified marker\n"
+ " -T Timestamp console output\n";
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Daemon::ProcessOption(int option)
+// Purpose: Processes the supplied option (equivalent to the
+// return code from getopt()). Return zero if the
+// option was handled successfully, or nonzero to
+// abort the program with that return value.
+// Created: 2007/09/18
+//
+// --------------------------------------------------------------------------
+int Daemon::ProcessOption(signed int option)
+{
+ switch(option)
{
- const char *banner = DaemonBanner();
- if(banner != 0)
+ case 'c':
{
- printf("%s", banner);
+ mConfigFileName = optarg;
+ mHaveConfigFile = true;
}
- }
+ break;
- std::string pidFileName;
+#ifndef WIN32
+ case 'D':
+ {
+ mSingleProcess = true;
+ }
+ break;
- try
- {
- // Find filename of config file
- mConfigFileName = DefaultConfigFile;
- if(argc >= 2)
+ case 'F':
{
- // First argument is config file, or it's -c and the next arg is the config file
- if(::strcmp(argv[1], "-c") == 0 && argc >= 3)
- {
- mConfigFileName = argv[2];
- }
- else
+ mRunInForeground = true;
+ }
+ break;
+
+ case 'k':
+ {
+ mKeepConsoleOpenAfterFork = true;
+ }
+ break;
+#endif
+
+ case 'h':
+ {
+ Usage();
+ return 2;
+ }
+ break;
+
+ case 'q':
+ {
+ if(mLogLevel == Log::NOTHING)
{
- mConfigFileName = argv[1];
+ BOX_FATAL("Too many '-q': "
+ "Cannot reduce logging "
+ "level any more");
+ return 2;
}
+ mLogLevel--;
}
-
- // Test mode with no daemonisation?
- bool asDaemon = true;
- if(argc >= 3)
+ break;
+
+ case 'v':
{
- if(::strcmp(argv[2], "SINGLEPROCESS") == 0)
+ if(mLogLevel == Log::EVERYTHING)
{
- asDaemon = false;
+ BOX_FATAL("Too many '-v': "
+ "Cannot increase logging "
+ "level any more");
+ return 2;
}
+ mLogLevel++;
+ }
+ break;
+
+ case 'V':
+ {
+ mLogLevel = Log::EVERYTHING;
+ }
+ break;
+
+ case 't':
+ {
+ Console::SetTag(optarg);
+ }
+ break;
+
+ case 'T':
+ {
+ Console::SetShowTime(true);
+ }
+ break;
+
+ case '?':
+ {
+ BOX_FATAL("Unknown option on command line: "
+ << "'" << (char)optopt << "'");
+ return 2;
+ }
+ break;
+
+ default:
+ {
+ BOX_FATAL("Unknown error in getopt: returned "
+ << "'" << option << "'");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Daemon::Main(const char *, int, const char *[])
+// Purpose: Parses command-line options, and then calls
+// Main(std::string& configFile, bool singleProcess)
+// to start the daemon.
+// Created: 2003/07/29
+//
+// --------------------------------------------------------------------------
+int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
+{
+ // Find filename of config file
+ mConfigFileName = DefaultConfigFile;
+ mAppName = argv[0];
+
+ #ifdef NDEBUG
+ mLogLevel = Log::NOTICE; // need an int to do math with
+ #else
+ mLogLevel = Log::INFO; // need an int to do math with
+ #endif
+
+ if (argc == 2 && strcmp(argv[1], "/?") == 0)
+ {
+ Usage();
+ return 2;
+ }
+
+ signed int c;
+
+ // 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
+ optind = 1;
+ optreset = 1;
+ #elif defined __GLIBC__
+ optind = 0;
+ #else // Solaris, any others?
+ optind = 1;
+ #endif
+
+ while((c = getopt(argc, (char * const *)argv,
+ GetOptionString().c_str())) != -1)
+ {
+ int returnCode = ProcessOption(c);
+
+ if (returnCode != 0)
+ {
+ return returnCode;
}
+ }
+ if (argc > optind && !mHaveConfigFile)
+ {
+ mConfigFileName = argv[optind]; optind++;
+ }
+
+ if (argc > optind && ::strcmp(argv[optind], "SINGLEPROCESS") == 0)
+ {
+ mSingleProcess = true; optind++;
+ }
+
+ if (argc > optind)
+ {
+ BOX_FATAL("Unknown parameter on command line: "
+ << "'" << std::string(argv[optind]) << "'");
+ return 2;
+ }
+
+ Logging::SetGlobalLevel((Log::Level)mLogLevel);
+
+ return Main(mConfigFileName);
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Daemon::Main(const std::string& rConfigFileName)
+// Purpose: Starts the daemon off -- equivalent of C main() function
+// Created: 2003/07/29
+//
+// --------------------------------------------------------------------------
+int Daemon::Main(const std::string &rConfigFileName)
+{
+ // Banner (optional)
+ {
+ #ifndef NDEBUG
+ BOX_NOTICE(DaemonBanner());
+ #endif
+ }
+
+ std::string pidFileName;
+
+ mConfigFileName = rConfigFileName;
+
+ bool asDaemon = !mSingleProcess && !mRunInForeground;
+
+ try
+ {
// Load the configuration file.
std::string errors;
std::auto_ptr<Configuration> pconfig;
@@ -178,16 +397,9 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(e.GetType() == CommonException::ExceptionType &&
e.GetSubType() == CommonException::OSFileOpenError)
{
- fprintf(stderr, "%s: failed to start: "
- "failed to open configuration file: "
- "%s", DaemonName(),
- mConfigFileName.c_str());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: failed to start: "
- "failed to open configuration file: "
- "%s", DaemonName(),
- mConfigFileName.c_str());
-#endif
+ BOX_FATAL("Failed to start: failed to open "
+ "configuration file: "
+ << mConfigFileName);
return 1;
}
@@ -198,14 +410,8 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(pconfig.get() == 0 || !errors.empty())
{
// Tell user about errors
- fprintf(stderr, "%s: Errors in config file %s:\n%s",
- DaemonName(), mConfigFileName.c_str(),
- errors.c_str());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: Errors in config file %s:\n%s",
- DaemonName(), mConfigFileName.c_str(),
- errors.c_str());
-#endif
+ BOX_FATAL("Failed to start: errors in configuration "
+ "file: " << mConfigFileName << ": " << errors);
// And give up
return 1;
}
@@ -217,17 +423,6 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// Let the derived class have a go at setting up stuff in the initial process
SetupInInitialProcess();
-#ifndef WIN32
- // Set signal handler
- 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)
- {
- THROW_EXCEPTION(ServerException, DaemoniseFailed)
- }
-
// Server configuration
const Configuration &serverConfig(
mpConfiguration->GetSubConfiguration("Server"));
@@ -235,7 +430,8 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// 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
// Handle changing to a different user
if(serverConfig.KeyExists("User"))
{
@@ -264,7 +460,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
default:
// parent
- _exit(0);
+ // _exit(0);
return 0;
break;
@@ -278,7 +474,8 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// Set new session
if(::setsid() == -1)
{
- ::syslog(LOG_ERR, "can't setsid");
+ BOX_ERROR("Failed to setsid(): " <<
+ strerror(errno));
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
@@ -301,24 +498,39 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
break;
}
}
-#endif // ! WIN32
- // open the log
- ::openlog(DaemonName(), LOG_PID, LOG_LOCAL6);
+ // Set signal handler
+ // Don't do this in the parent, since it might be anything
+ // (e.g. test/bbackupd)
+
+ 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)
+ {
+ THROW_EXCEPTION(ServerException, DaemoniseFailed)
+ }
+#endif // !WIN32
+
// Log the start message
- ::syslog(LOG_INFO, "Starting daemon (config: %s) (version "
- BOX_VERSION ")", mConfigFileName.c_str());
+ BOX_NOTICE("Starting daemon, version " << BOX_VERSION
+ << ", config: " << mConfigFileName);
-#ifndef WIN32
// 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)
{
- ::syslog(LOG_ERR, "can't write pid file");
+ BOX_FATAL("can't write pid file");
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
-#endif
// Set up memory leak reporting
#ifdef BOX_MEMORY_LEAK_TESTING
@@ -329,7 +541,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
}
#endif // BOX_MEMORY_LEAK_TESTING
- if(asDaemon)
+ if(asDaemon && !mKeepConsoleOpenAfterFork)
{
#ifndef WIN32
// Close standard streams
@@ -352,44 +564,47 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
{
::close(devnull);
}
-#endif // ! WIN32
// 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
}
}
catch(BoxException &e)
{
- fprintf(stderr, "%s: failed to start: exception %s (%d/%d)\n",
- DaemonName(), e.what(), e.GetType(), e.GetSubType());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: failed to start: "
- "exception %s (%d/%d)\n", DaemonName(),
- e.what(), e.GetType(), e.GetSubType());
-#endif
+ BOX_FATAL("Failed to start: exception " << e.what()
+ << " (" << e.GetType()
+ << "/" << e.GetSubType() << ")");
return 1;
}
catch(std::exception &e)
{
- fprintf(stderr, "%s: failed to start: exception %s\n",
- DaemonName(), e.what());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: failed to start: exception %s\n",
- DaemonName(), e.what());
-#endif
+ BOX_FATAL("Failed to start: exception " << e.what());
return 1;
}
catch(...)
{
- fprintf(stderr, "%s: failed to start: unknown exception\n",
- DaemonName());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: failed to start: unknown exception\n",
- DaemonName());
-#endif
+ BOX_FATAL("Failed to start: unknown error");
return 1;
}
+
+#ifdef WIN32
+ // Under win32 we must initialise the Winsock library
+ // before using sockets
+
+ WSADATA info;
+
+ if (WSAStartup(0x0101, &info) == SOCKET_ERROR)
+ {
+ // will not run without sockets
+ BOX_FATAL("Failed to initialise Windows Sockets");
+ THROW_EXCEPTION(CommonException, Internal)
+ }
+#endif
+
+ int retcode = 0;
// Main Daemon running
try
@@ -401,9 +616,8 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(mReloadConfigWanted && !mTerminateWanted)
{
// Need to reload that config file...
- ::syslog(LOG_INFO, "Reloading configuration "
- "(config: %s)",
- mConfigFileName.c_str());
+ BOX_NOTICE("Reloading configuration file: "
+ << mConfigFileName);
std::string errors;
std::auto_ptr<Configuration> pconfig =
Configuration::LoadAndVerify(
@@ -414,12 +628,12 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(pconfig.get() == 0 || !errors.empty())
{
// Tell user about errors
- ::syslog(LOG_ERR, "Errors in config "
- "file %s:\n%s",
- mConfigFileName.c_str(),
- errors.c_str());
+ BOX_FATAL("Error in configuration "
+ << "file: " << mConfigFileName
+ << ": " << errors);
// And give up
- return 1;
+ retcode = 1;
+ break;
}
// delete old configuration
@@ -440,29 +654,31 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
::unlink(pidFileName.c_str());
// Log
- ::syslog(LOG_INFO, "Terminating daemon");
+ BOX_NOTICE("Terminating daemon");
}
catch(BoxException &e)
{
- ::syslog(LOG_ERR, "%s: terminating due to exception %s "
- "(%d/%d)", DaemonName(), e.what(), e.GetType(),
- e.GetSubType());
- return 1;
+ BOX_FATAL("Terminating due to exception " << e.what()
+ << " (" << e.GetType()
+ << "/" << e.GetSubType() << ")");
+ retcode = 1;
}
catch(std::exception &e)
{
- ::syslog(LOG_ERR, "%s: terminating due to exception %s",
- DaemonName(), e.what());
- return 1;
+ BOX_FATAL("Terminating due to exception " << e.what());
+ retcode = 1;
}
catch(...)
{
- ::syslog(LOG_ERR, "%s: terminating due to unknown exception",
- DaemonName());
- return 1;
+ BOX_FATAL("Terminating due to unknown exception");
+ retcode = 1;
}
+
+#ifdef WIN32
+ WSACleanup();
+#endif
- return 0;
+ return retcode;
}
// --------------------------------------------------------------------------
@@ -539,9 +755,9 @@ const char *Daemon::DaemonName() const
// Created: 1/1/04
//
// --------------------------------------------------------------------------
-const char *Daemon::DaemonBanner() const
+std::string Daemon::DaemonBanner() const
{
- return 0;
+ return "Generic daemon using the Box Application Framework";
}
diff --git a/lib/server/Daemon.h b/lib/server/Daemon.h
index ff36fba4..a500ec6a 100644
--- a/lib/server/Daemon.h
+++ b/lib/server/Daemon.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -79,14 +79,18 @@ private:
public:
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);
virtual void Run();
const Configuration &GetConfiguration() const;
const std::string &GetConfigFileName() const {return mConfigFileName;}
virtual const char *DaemonName() const;
- virtual const char *DaemonBanner() const;
+ virtual std::string DaemonBanner() const;
virtual const ConfigurationVerify *GetConfigVerify() const;
+ virtual void Usage();
bool StopRun() {return mReloadConfigWanted | mTerminateWanted;}
bool IsReloadConfigWanted() {return mReloadConfigWanted;}
@@ -103,18 +107,26 @@ public:
protected:
box_time_t GetLoadedConfigModifiedTime() const;
+ bool IsSingleProcess() { return mSingleProcess; }
+ virtual std::string GetOptionString();
+ virtual int ProcessOption(signed int option);
private:
static void SignalHandler(int sigraised);
box_time_t GetConfigFileModifiedTime() const;
-private:
std::string mConfigFileName;
Configuration *mpConfiguration;
box_time_t mLoadedConfigModifiedTime;
bool mReloadConfigWanted;
bool mTerminateWanted;
+ bool mSingleProcess;
+ bool mRunInForeground;
+ bool mKeepConsoleOpenAfterFork;
+ bool mHaveConfigFile;
+ int mLogLevel; // need an int to do math with
static Daemon *spDaemon;
+ std::string mAppName;
};
#define DAEMON_VERIFY_SERVER_KEYS {"PidFile", 0, ConfigTest_Exists, 0}, \
diff --git a/lib/server/LocalProcessStream.cpp b/lib/server/LocalProcessStream.cpp
index 7b8e78c6..f694fa58 100644
--- a/lib/server/LocalProcessStream.cpp
+++ b/lib/server/LocalProcessStream.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -56,10 +56,15 @@
#endif
#include "LocalProcessStream.h"
-#include "SocketStream.h"
#include "autogen_ServerException.h"
#include "Utils.h"
+#ifdef WIN32
+ #include "FileStream.h"
+#else
+ #include "SocketStream.h"
+#endif
+
#include "MemLeakFindOn.h"
#define MAX_ARGUMENTS 64
@@ -68,19 +73,22 @@
//
// Function
// Name: LocalProcessStream(const char *, pid_t &)
-// Purpose: Run a new process, and return a stream giving access to it's
-// stdin and stdout. Returns the PID of the new process -- this
-// must be waited on at some point to avoid zombies.
+// Purpose: Run a new process, and return a stream giving access
+// to its stdin and stdout (stdout and stderr on
+// Win32). Returns the PID of the new process -- this
+// must be waited on at some point to avoid zombies
+// (except on Win32).
// Created: 12/3/04
//
// --------------------------------------------------------------------------
std::auto_ptr<IOStream> LocalProcessStream(const char *CommandLine, pid_t &rPidOut)
{
+#ifndef WIN32
+
// Split up command
std::vector<std::string> command;
SplitString(std::string(CommandLine), ' ', command);
-#ifndef WIN32
// Build arguments
char *args[MAX_ARGUMENTS + 4];
{
@@ -139,10 +147,68 @@ std::auto_ptr<IOStream> LocalProcessStream(const char *CommandLine, pid_t &rPidO
// Return the stream object and PID
rPidOut = pid;
return stream;
+
#else // WIN32
- ::syslog(LOG_ERR, "vfork not implemented - LocalProcessStream.cpp");
- std::auto_ptr<IOStream> stream;
+
+ SECURITY_ATTRIBUTES secAttr;
+ secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ secAttr.bInheritHandle = TRUE;
+ secAttr.lpSecurityDescriptor = NULL;
+
+ HANDLE writeInChild, readFromChild;
+ if(!CreatePipe(&readFromChild, &writeInChild, &secAttr, 0))
+ {
+ BOX_ERROR("Failed to CreatePipe for child process: " <<
+ GetErrorMessage(GetLastError()));
+ THROW_EXCEPTION(ServerException, SocketPairFailed)
+ }
+ SetHandleInformation(readFromChild, HANDLE_FLAG_INHERIT, 0);
+
+ PROCESS_INFORMATION procInfo;
+ STARTUPINFO startupInfo;
+
+ ZeroMemory(&procInfo, sizeof(procInfo));
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+ startupInfo.hStdError = writeInChild;
+ startupInfo.hStdOutput = writeInChild;
+ startupInfo.hStdInput = INVALID_HANDLE_VALUE;
+ startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ CHAR* commandLineCopy = (CHAR*)malloc(strlen(CommandLine) + 1);
+ strcpy(commandLineCopy, CommandLine);
+
+ BOOL result = CreateProcess(NULL,
+ commandLineCopy, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &startupInfo, // STARTUPINFO pointer
+ &procInfo); // receives PROCESS_INFORMATION
+
+ free(commandLineCopy);
+
+ if(!result)
+ {
+ BOX_ERROR("Failed to CreateProcess: '" << CommandLine <<
+ "': " << GetErrorMessage(GetLastError()));
+ CloseHandle(writeInChild);
+ CloseHandle(readFromChild);
+ THROW_EXCEPTION(ServerException, ServerForkError)
+ }
+
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+ CloseHandle(writeInChild);
+
+ rPidOut = (int)(procInfo.dwProcessId);
+
+ std::auto_ptr<IOStream> stream(new FileStream(readFromChild));
return stream;
+
#endif // ! WIN32
}
diff --git a/lib/server/LocalProcessStream.h b/lib/server/LocalProcessStream.h
index 386ae21f..5547c79c 100644
--- a/lib/server/LocalProcessStream.h
+++ b/lib/server/LocalProcessStream.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/Makefile.extra b/lib/server/Makefile.extra
index 6cc0de2e..33fac0a1 100644
--- a/lib/server/Makefile.extra
+++ b/lib/server/Makefile.extra
@@ -3,9 +3,9 @@ MAKEEXCEPTION = ../../lib/common/makeexception.pl
# AUTOGEN SEEDING
autogen_ServerException.h autogen_ServerException.cpp: $(MAKEEXCEPTION) ServerException.txt
- perl $(MAKEEXCEPTION) ServerException.txt
+ $(PERL) $(MAKEEXCEPTION) ServerException.txt
# AUTOGEN SEEDING
autogen_ConnectionException.h autogen_ConnectionException.cpp: $(MAKEEXCEPTION) ConnectionException.txt
- perl $(MAKEEXCEPTION) ConnectionException.txt
+ $(PERL) $(MAKEEXCEPTION) ConnectionException.txt
diff --git a/lib/server/Protocol.cpp b/lib/server/Protocol.cpp
index d459bdcb..0f15beeb 100644
--- a/lib/server/Protocol.cpp
+++ b/lib/server/Protocol.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -60,6 +60,7 @@
#include "ServerException.h"
#include "PartialReadStream.h"
#include "ProtocolUncertainStream.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -93,7 +94,8 @@ Protocol::Protocol(IOStream &rStream)
mLastErrorType(NoError),
mLastErrorSubType(NoError)
{
- TRACE1("Send block allocation size is %d\n", PROTOCOL_ALLOCATE_SEND_BLOCK_CHUNK);
+ BOX_TRACE("Send block allocation size is " <<
+ PROTOCOL_ALLOCATE_SEND_BLOCK_CHUNK);
}
// --------------------------------------------------------------------------
diff --git a/lib/server/Protocol.h b/lib/server/Protocol.h
index 7781ac7c..da665219 100644
--- a/lib/server/Protocol.h
+++ b/lib/server/Protocol.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/ProtocolObject.cpp b/lib/server/ProtocolObject.cpp
index 44466392..6b426141 100644
--- a/lib/server/ProtocolObject.cpp
+++ b/lib/server/ProtocolObject.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/ProtocolObject.h b/lib/server/ProtocolObject.h
index 0dbb2817..9b6736fa 100644
--- a/lib/server/ProtocolObject.h
+++ b/lib/server/ProtocolObject.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/ProtocolUncertainStream.cpp b/lib/server/ProtocolUncertainStream.cpp
index 5d22dc89..84ef6e38 100644
--- a/lib/server/ProtocolUncertainStream.cpp
+++ b/lib/server/ProtocolUncertainStream.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/ProtocolUncertainStream.h b/lib/server/ProtocolUncertainStream.h
index ea5346e7..6c4e88f4 100644
--- a/lib/server/ProtocolUncertainStream.h
+++ b/lib/server/ProtocolUncertainStream.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -39,7 +39,7 @@
// --------------------------------------------------------------------------
//
// File
-// Name: PartialReadStream.h
+// Name: ProtocolUncertainStream.h
// Purpose: Read part of another stream
// Created: 2003/12/05
//
@@ -53,7 +53,7 @@
// --------------------------------------------------------------------------
//
// Class
-// Name: PartialReadStream
+// Name: ProtocolUncertainStream
// Purpose: Read part of another stream
// Created: 2003/12/05
//
diff --git a/lib/server/ProtocolWire.h b/lib/server/ProtocolWire.h
index 11add21e..64326f31 100644
--- a/lib/server/ProtocolWire.h
+++ b/lib/server/ProtocolWire.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/SSLLib.cpp b/lib/server/SSLLib.cpp
index b8316bc0..40b51da6 100644
--- a/lib/server/SSLLib.cpp
+++ b/lib/server/SSLLib.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -52,10 +52,6 @@
#include <openssl/err.h>
#include <openssl/rand.h>
-#ifndef WIN32
-#include <syslog.h>
-#endif
-
#include "SSLLib.h"
#include "ServerException.h"
@@ -91,7 +87,8 @@ void SSLLib::Initialise()
THROW_EXCEPTION(ServerException, SSLRandomInitFailed)
}
#else
- ::fprintf(stderr, "No random device -- additional seeding of random number generator not performed.\n");
+ BOX_WARNING("No random device -- additional seeding of "
+ "random number generator not performed.");
#endif
}
@@ -111,13 +108,8 @@ void SSLLib::LogError(const char *ErrorDuringAction)
while((errcode = ERR_get_error()) != 0)
{
::ERR_error_string_n(errcode, errname, sizeof(errname));
- #ifndef NDEBUG
- if(SSLLib__TraceErrors)
- {
- TRACE2("SSL err during %s: %s\n", ErrorDuringAction, errname);
- }
- #endif
- ::syslog(LOG_ERR, "SSL err during %s: %s", ErrorDuringAction, errname);
+ BOX_ERROR("SSL error during " << ErrorDuringAction << ": " <<
+ errname);
}
}
diff --git a/lib/server/SSLLib.h b/lib/server/SSLLib.h
index b6fa9aad..7f0d5a2b 100644
--- a/lib/server/SSLLib.h
+++ b/lib/server/SSLLib.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/ServerControl.h b/lib/server/ServerControl.h
new file mode 100644
index 00000000..c8c746e4
--- /dev/null
+++ b/lib/server/ServerControl.h
@@ -0,0 +1,228 @@
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
+//
+// Copyright (c) 2003 - 2006
+// Ben Summers and contributors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. All use of this software and associated advertising materials must
+// display the following acknowledgment:
+// This product includes software developed by Ben Summers.
+// 4. The names of the Authors may not be used to endorse or promote
+// products derived from this software without specific prior written
+// permission.
+//
+// [Where legally impermissible the Authors do not disclaim liability for
+// direct physical injury or death caused solely by defects in the software
+// unless it is modified by a third party.]
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+//
+//
+#ifndef SERVER_CONTROL_H
+#define SERVER_CONTROL_H
+
+#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");
+}
+
+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;
+}
+
+#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/ServerException.h b/lib/server/ServerException.h
index f6e59371..2d44271f 100644
--- a/lib/server/ServerException.h
+++ b/lib/server/ServerException.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/ServerStream.h b/lib/server/ServerStream.h
index e1136c72..0e81d05e 100644
--- a/lib/server/ServerStream.h
+++ b/lib/server/ServerStream.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -52,7 +52,6 @@
#include <errno.h>
#ifndef WIN32
- #include <syslog.h>
#include <sys/wait.h>
#endif
@@ -94,9 +93,11 @@ public:
return "generic-stream-server";
}
+ virtual void OnIdle() { }
+
virtual void Run()
{
- // Set process title as appropraite
+ // Set process title as appropriate
SetProcessTitle(ForkToHandleRequests?"server":"idle");
// Handle exceptions and child task quitting gracefully.
@@ -109,7 +110,10 @@ public:
{
if(childExit)
{
- ::syslog(LOG_ERR, "in server child, exception %s (%d/%d) -- terminating child", e.what(), e.GetType(), e.GetSubType());
+ BOX_ERROR("Error in child process, "
+ "terminating connection: exception " <<
+ e.what() << "(" << e.GetType() <<
+ "/" << e.GetSubType() << ")");
_exit(1);
}
else throw;
@@ -118,7 +122,9 @@ public:
{
if(childExit)
{
- ::syslog(LOG_ERR, "in server child, exception %s -- terminating child", e.what());
+ BOX_ERROR("Error in child process, "
+ "terminating connection: exception " <<
+ e.what());
_exit(1);
}
else throw;
@@ -127,7 +133,9 @@ public:
{
if(childExit)
{
- ::syslog(LOG_ERR, "in server child, unknown exception -- terminating child");
+ BOX_ERROR("Error in child process, "
+ "terminating connection: "
+ "unknown exception");
_exit(1);
}
else throw;
@@ -206,16 +214,22 @@ public:
}
else if(c[0] == "unix")
{
- // Check arguments size
- if(c.size() != 2)
- {
- THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
- }
+ #ifdef WIN32
+ BOX_WARNING("Ignoring request to listen on a Unix socket on Windows: " << addrlist[a]);
+ delete psocket;
+ psocket = NULL;
+ #else
+ // Check arguments size
+ if(c.size() != 2)
+ {
+ THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
+ }
- // unlink anything there
- ::unlink(c[1].c_str());
-
- psocket->Listen(Socket::TypeUNIX, c[1].c_str());
+ // unlink anything there
+ ::unlink(c[1].c_str());
+
+ psocket->Listen(Socket::TypeUNIX, c[1].c_str());
+ #endif // WIN32
}
else
{
@@ -223,8 +237,11 @@ public:
THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
}
- // Add to list of sockets
- mSockets.push_back(psocket);
+ if (psocket != NULL)
+ {
+ // Add to list of sockets
+ mSockets.push_back(psocket);
+ }
}
catch(...)
{
@@ -232,8 +249,11 @@ public:
throw;
}
- // Add to the list of things to wait on
- connectionWait.Add(psocket);
+ if (psocket != NULL)
+ {
+ // Add to the list of things to wait on
+ connectionWait.Add(psocket);
+ }
}
}
@@ -245,7 +265,8 @@ public:
if(psocket)
{
- // Get the incomming connection (with zero wait time)
+ // Get the incoming connection
+ // (with zero wait time)
std::string logMessage;
std::auto_ptr<StreamType> connection(psocket->Accept(0, &logMessage));
@@ -253,7 +274,8 @@ public:
if(connection.get())
{
// Since this is a template parameter, the if() will be optimised out by the compiler
- if(ForkToHandleRequests)
+ #ifndef WIN32 // no fork on Win32
+ if(ForkToHandleRequests && !IsSingleProcess())
{
pid_t pid = ::fork();
switch(pid)
@@ -289,20 +311,26 @@ public:
}
// Log it
- ::syslog(LOG_INFO, "%s (handling in child %d)", logMessage.c_str(), pid);
+ BOX_WARNING("Message from child process " << pid << ": " << logMessage);
}
else
{
- // Just handle in this connection
+ #endif // !WIN32
+ // Just handle in this process
SetProcessTitle("handling");
HandleConnection(*connection);
SetProcessTitle("idle");
+ #ifndef WIN32
}
+ #endif // !WIN32
}
}
-
+
+ OnIdle();
+
+ #ifndef WIN32
// Clean up child processes (if forking daemon)
- if(ForkToHandleRequests)
+ if(ForkToHandleRequests && !IsSingleProcess())
{
int status = 0;
int p = 0;
@@ -315,6 +343,7 @@ public:
}
} while(p > 0);
}
+ #endif // !WIN32
}
}
catch(...)
@@ -335,11 +364,15 @@ public:
virtual void Connection(StreamType &rStream) = 0;
protected:
- // For checking code in dervied classes -- use if you have an algorithm which
+ // For checking code in derived classes -- use if you have an algorithm which
// depends on the forking model in case someone changes it later.
bool WillForkToHandleRequests()
{
- return ForkToHandleRequests;
+ #ifdef WIN32
+ return false;
+ #else
+ return ForkToHandleRequests && !IsSingleProcess();
+ #endif // WIN32
}
private:
diff --git a/lib/server/ServerTLS.h b/lib/server/ServerTLS.h
index be53af4c..a74f0087 100644
--- a/lib/server/ServerTLS.h
+++ b/lib/server/ServerTLS.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/Socket.cpp b/lib/server/Socket.cpp
index 0939343d..95347af6 100644
--- a/lib/server/Socket.cpp
+++ b/lib/server/Socket.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -55,7 +55,6 @@
#ifndef WIN32
#include <sys/socket.h>
#include <netdb.h>
-#include <syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
@@ -162,18 +161,20 @@ void Socket::LogIncomingConnection(const struct sockaddr *addr, socklen_t addrle
switch(addr->sa_family)
{
case AF_UNIX:
- ::syslog(LOG_INFO, "Incoming connection from local (UNIX socket)");
+ BOX_INFO("Incoming connection from local (UNIX socket)");
break;
case AF_INET:
{
sockaddr_in *a = (sockaddr_in*)addr;
- ::syslog(LOG_INFO, "Incoming connection from %s port %d", inet_ntoa(a->sin_addr), ntohs(a->sin_port));
+ BOX_INFO("Incoming connection from " <<
+ inet_ntoa(a->sin_addr) << " port " <<
+ ntohs(a->sin_port));
}
break;
default:
- ::syslog(LOG_INFO, "Incoming connection of unknown type");
+ BOX_WARNING("Incoming connection of unknown type");
break;
}
}
diff --git a/lib/server/Socket.h b/lib/server/Socket.h
index fc3a3a0c..f262bc3a 100644
--- a/lib/server/Socket.h
+++ b/lib/server/Socket.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/SocketListen.h b/lib/server/SocketListen.h
index 41a9fc3f..7b99631b 100644
--- a/lib/server/SocketListen.h
+++ b/lib/server/SocketListen.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/SocketStream.cpp b/lib/server/SocketStream.cpp
index 49bf18b2..38370598 100644
--- a/lib/server/SocketStream.cpp
+++ b/lib/server/SocketStream.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -53,6 +53,7 @@
#include <sys/types.h>
#include <errno.h>
+#include <string.h>
#ifndef WIN32
#include <poll.h>
@@ -74,7 +75,7 @@
//
// --------------------------------------------------------------------------
SocketStream::SocketStream()
- : mSocketHandle(-1),
+ : mSocketHandle(INVALID_SOCKET_VALUE),
mReadClosed(false),
mWriteClosed(false),
mBytesRead(0),
@@ -123,7 +124,7 @@ SocketStream::SocketStream(const SocketStream &rToCopy)
{
THROW_EXCEPTION(ServerException, BadSocketHandle);
}
- if(mSocketHandle == -1)
+ if(mSocketHandle == INVALID_SOCKET_VALUE)
{
THROW_EXCEPTION(ServerException, DupError);
}
@@ -139,7 +140,7 @@ SocketStream::SocketStream(const SocketStream &rToCopy)
// --------------------------------------------------------------------------
SocketStream::~SocketStream()
{
- if(mSocketHandle != -1)
+ if(mSocketHandle != INVALID_SOCKET_VALUE)
{
Close();
}
@@ -155,7 +156,10 @@ SocketStream::~SocketStream()
// --------------------------------------------------------------------------
void SocketStream::Attach(int socket)
{
- if(mSocketHandle != -1) {THROW_EXCEPTION(ServerException, SocketAlreadyOpen)}
+ if(mSocketHandle != INVALID_SOCKET_VALUE)
+ {
+ THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
+ }
mSocketHandle = socket;
ResetCounters();
@@ -172,7 +176,10 @@ void SocketStream::Attach(int socket)
// --------------------------------------------------------------------------
void SocketStream::Open(int Type, const char *Name, int Port)
{
- if(mSocketHandle != -1) {THROW_EXCEPTION(ServerException, SocketAlreadyOpen)}
+ if(mSocketHandle != INVALID_SOCKET_VALUE)
+ {
+ THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
+ }
// Setup parameters based on type, looking up names if required
int sockDomain = 0;
@@ -182,7 +189,7 @@ void SocketStream::Open(int Type, const char *Name, int Port)
// Create the socket
mSocketHandle = ::socket(sockDomain, SOCK_STREAM, 0 /* let OS choose protocol */);
- if(mSocketHandle == -1)
+ if(mSocketHandle == INVALID_SOCKET_VALUE)
{
THROW_EXCEPTION(ServerException, SocketOpenError)
}
@@ -192,13 +199,29 @@ void SocketStream::Open(int Type, const char *Name, int Port)
{
// Dispose of the socket
#ifdef WIN32
+ DWORD err = WSAGetLastError();
::closesocket(mSocketHandle);
#else
+ int err = errno;
::close(mSocketHandle);
#endif
- mSocketHandle = -1;
+
+#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
+
+ mSocketHandle = INVALID_SOCKET_VALUE;
THROW_EXCEPTION(ConnectionException, Conn_SocketConnectError)
}
+
ResetCounters();
}
@@ -212,7 +235,10 @@ void SocketStream::Open(int Type, const char *Name, int Port)
// --------------------------------------------------------------------------
int SocketStream::Read(void *pBuffer, int NBytes, int Timeout)
{
- if(mSocketHandle == -1) {THROW_EXCEPTION(ServerException, BadSocketHandle)}
+ if(mSocketHandle == INVALID_SOCKET_VALUE)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
if(Timeout != IOStream::TimeOutInfinite)
{
@@ -285,7 +311,10 @@ int SocketStream::Read(void *pBuffer, int NBytes, int Timeout)
// --------------------------------------------------------------------------
void SocketStream::Write(const void *pBuffer, int NBytes)
{
- if(mSocketHandle == -1) {THROW_EXCEPTION(ServerException, BadSocketHandle)}
+ if(mSocketHandle == INVALID_SOCKET_VALUE)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
// Buffer in byte sized type.
ASSERT(sizeof(char) == 1);
@@ -349,7 +378,10 @@ void SocketStream::Write(const void *pBuffer, int NBytes)
// --------------------------------------------------------------------------
void SocketStream::Close()
{
- if(mSocketHandle == -1) {THROW_EXCEPTION(ServerException, BadSocketHandle)}
+ if(mSocketHandle == INVALID_SOCKET_VALUE)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
#ifdef WIN32
if(::closesocket(mSocketHandle) == -1)
#else
@@ -358,7 +390,7 @@ void SocketStream::Close()
{
THROW_EXCEPTION(ServerException, SocketCloseError)
}
- mSocketHandle = -1;
+ mSocketHandle = INVALID_SOCKET_VALUE;
}
// --------------------------------------------------------------------------
@@ -371,7 +403,10 @@ void SocketStream::Close()
// --------------------------------------------------------------------------
void SocketStream::Shutdown(bool Read, bool Write)
{
- if(mSocketHandle == -1) {THROW_EXCEPTION(ServerException, BadSocketHandle)}
+ if(mSocketHandle == INVALID_SOCKET_VALUE)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
// Do anything?
if(!Read && !Write) return;
@@ -426,7 +461,10 @@ bool SocketStream::StreamClosed()
// --------------------------------------------------------------------------
tOSSocketHandle SocketStream::GetSocketHandle()
{
- if(mSocketHandle == -1) {THROW_EXCEPTION(ServerException, BadSocketHandle)}
+ if(mSocketHandle == INVALID_SOCKET_VALUE)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
return mSocketHandle;
}
diff --git a/lib/server/SocketStream.h b/lib/server/SocketStream.h
index 17a2e4d7..efe3f9ce 100644
--- a/lib/server/SocketStream.h
+++ b/lib/server/SocketStream.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -52,8 +52,10 @@
#ifdef WIN32
typedef SOCKET tOSSocketHandle;
+ #define INVALID_SOCKET_VALUE (tOSSocketHandle)(-1)
#else
typedef int tOSSocketHandle;
+ #define INVALID_SOCKET_VALUE -1
#endif
// --------------------------------------------------------------------------
@@ -103,6 +105,7 @@ public:
off_t GetBytesRead() const {return mBytesRead;}
off_t GetBytesWritten() const {return mBytesWritten;}
void ResetCounters() {mBytesRead = mBytesWritten = 0;}
+ bool IsOpened() { return mSocketHandle != INVALID_SOCKET_VALUE; }
};
#endif // SOCKETSTREAM__H
diff --git a/lib/server/SocketStreamTLS.cpp b/lib/server/SocketStreamTLS.cpp
index 0845f868..e7126120 100644
--- a/lib/server/SocketStreamTLS.cpp
+++ b/lib/server/SocketStreamTLS.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -61,6 +61,7 @@
#include "SSLLib.h"
#include "ServerException.h"
#include "TLSContext.h"
+#include "BoxTime.h"
#include "MemLeakFindOn.h"
@@ -175,8 +176,12 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer)
THROW_EXCEPTION(ServerException, TLSAllocationFailed)
}
-#ifndef WIN32
// Make the socket non-blocking so timeouts on Read work
+
+#ifdef WIN32
+ u_long nonblocking = 1;
+ ioctlsocket(socket, FIONBIO, &nonblocking);
+#else // !WIN32
// This is more portable than using ioctl with FIONBIO
int statusFlags = 0;
if(::fcntl(socket, F_GETFL, &statusFlags) < 0
@@ -278,20 +283,30 @@ bool SocketStreamTLS::WaitWhenRetryRequired(int SSLErrorCode, int Timeout)
break;
}
p.revents = 0;
- switch(::poll(&p, 1, (Timeout == IOStream::TimeOutInfinite)?INFTIM:Timeout))
+
+ int64_t start, end;
+ start = BoxTimeToMilliSeconds(GetCurrentBoxTime());
+ end = start + Timeout;
+ int result;
+
+ do
{
- case -1:
- // error
- if(errno == EINTR)
+ int64_t now = BoxTimeToMilliSeconds(GetCurrentBoxTime());
+ int poll_timeout = (int)(end - now);
+ if (poll_timeout < 0) poll_timeout = 0;
+ if (Timeout == IOStream::TimeOutInfinite)
{
- // Signal. Do "time out"
- return false;
- }
- else
- {
- // Bad!
- THROW_EXCEPTION(ServerException, SocketPollError)
+ poll_timeout = INFTIM;
}
+ result = ::poll(&p, 1, poll_timeout);
+ }
+ while(result == -1 && errno == EINTR);
+
+ switch(result)
+ {
+ case -1:
+ // error - Bad!
+ THROW_EXCEPTION(ServerException, SocketPollError)
break;
case 0:
@@ -347,7 +362,7 @@ int SocketStreamTLS::Read(void *pBuffer, int NBytes, int Timeout)
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
- // wait for the requried data
+ // wait for the required data
// Will only get once around this loop, so don't need to calculate timeout values
if(WaitWhenRetryRequired(se, Timeout) == false)
{
diff --git a/lib/server/SocketStreamTLS.h b/lib/server/SocketStreamTLS.h
index dc1da5bc..41f78d76 100644
--- a/lib/server/SocketStreamTLS.h
+++ b/lib/server/SocketStreamTLS.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/TLSContext.cpp b/lib/server/TLSContext.cpp
index 87df562c..da0b320c 100644
--- a/lib/server/TLSContext.cpp
+++ b/lib/server/TLSContext.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -99,6 +99,11 @@ TLSContext::~TLSContext()
// --------------------------------------------------------------------------
void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, const char *TrustedCAsFile)
{
+ if(mpContext != 0)
+ {
+ ::SSL_CTX_free(mpContext);
+ }
+
mpContext = ::SSL_CTX_new(AsServer?TLSv1_server_method():TLSv1_client_method());
if(mpContext == NULL)
{
diff --git a/lib/server/TLSContext.h b/lib/server/TLSContext.h
index 408b0039..88013396 100644
--- a/lib/server/TLSContext.h
+++ b/lib/server/TLSContext.h
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
diff --git a/lib/server/WinNamedPipeStream.cpp b/lib/server/WinNamedPipeStream.cpp
new file mode 100644
index 00000000..bb264da7
--- /dev/null
+++ b/lib/server/WinNamedPipeStream.cpp
@@ -0,0 +1,629 @@
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
+//
+// Copyright (c) 2003 - 2006
+// Ben Summers and contributors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. All use of this software and associated advertising materials must
+// display the following acknowledgment:
+// This product includes software developed by Ben Summers.
+// 4. The names of the Authors may not be used to endorse or promote
+// products derived from this software without specific prior written
+// permission.
+//
+// [Where legally impermissible the Authors do not disclaim liability for
+// direct physical injury or death caused solely by defects in the software
+// unless it is modified by a third party.]
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+//
+//
+// --------------------------------------------------------------------------
+//
+// File
+// Name: WinNamedPipeStream.cpp
+// Purpose: I/O stream interface for Win32 named pipes
+// Created: 2005/12/07
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#ifdef WIN32
+
+#ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <errno.h>
+#include <windows.h>
+
+#include "WinNamedPipeStream.h"
+#include "ServerException.h"
+#include "CommonException.h"
+#include "Socket.h"
+
+#include "MemLeakFindOn.h"
+
+std::string WinNamedPipeStream::sPipeNamePrefix = "\\\\.\\pipe\\";
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::WinNamedPipeStream()
+// Purpose: Constructor (create stream ready for Open() call)
+// Created: 2005/12/07
+//
+// --------------------------------------------------------------------------
+WinNamedPipeStream::WinNamedPipeStream()
+ : mSocketHandle(INVALID_HANDLE_VALUE),
+ mReadableEvent(INVALID_HANDLE_VALUE),
+ mBytesInBuffer(0),
+ mReadClosed(false),
+ mWriteClosed(false),
+ mIsServer(false),
+ mIsConnected(false)
+{
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::~WinNamedPipeStream()
+// Purpose: Destructor, closes stream if open
+// Created: 2005/12/07
+//
+// --------------------------------------------------------------------------
+WinNamedPipeStream::~WinNamedPipeStream()
+{
+ if (mSocketHandle != INVALID_HANDLE_VALUE)
+ {
+ try
+ {
+ Close();
+ }
+ catch (std::exception &e)
+ {
+ BOX_ERROR("Caught exception while destroying "
+ "named pipe, ignored: " << e.what());
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::Accept(const std::string& rName)
+// Purpose: Creates a new named pipe with the given name,
+// and wait for a connection on it
+// Created: 2005/12/07
+//
+// --------------------------------------------------------------------------
+void WinNamedPipeStream::Accept(const std::string& rName)
+{
+ if (mSocketHandle != INVALID_HANDLE_VALUE || mIsConnected)
+ {
+ THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
+ }
+
+ 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)
+ {
+ BOX_ERROR("Failed to CreateNamedPipeA(" << socket << "): " <<
+ GetErrorMessage(GetLastError()));
+ THROW_EXCEPTION(ServerException, SocketOpenError)
+ }
+
+ bool connected = ConnectNamedPipe(mSocketHandle, (LPOVERLAPPED) NULL);
+
+ if (!connected)
+ {
+ BOX_ERROR("Failed to ConnectNamedPipe(" << socket << "): " <<
+ GetErrorMessage(GetLastError()));
+ Close();
+ THROW_EXCEPTION(ServerException, SocketOpenError)
+ }
+
+ mBytesInBuffer = 0;
+ mReadClosed = false;
+ mWriteClosed = false;
+ mIsServer = true; // must flush and disconnect before closing
+ 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)
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::Connect(const std::string& rName)
+// Purpose: Opens a connection to a listening named pipe
+// Created: 2005/12/07
+//
+// --------------------------------------------------------------------------
+void WinNamedPipeStream::Connect(const std::string& rName)
+{
+ if (mSocketHandle != INVALID_HANDLE_VALUE || mIsConnected)
+ {
+ THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
+ }
+
+ std::string socket = sPipeNamePrefix + rName;
+
+ mSocketHandle = CreateFileA(
+ socket.c_str(), // pipe name
+ GENERIC_READ | // read and write access
+ GENERIC_WRITE,
+ 0, // no sharing
+ NULL, // default security attributes
+ OPEN_EXISTING,
+ 0, // default attributes
+ NULL); // no template file
+
+ if (mSocketHandle == INVALID_HANDLE_VALUE)
+ {
+ DWORD err = GetLastError();
+ if (err == ERROR_PIPE_BUSY)
+ {
+ BOX_ERROR("Failed to connect to backup daemon: "
+ "it is busy with another connection");
+ }
+ else
+ {
+ BOX_ERROR("Failed to connect to backup daemon: " <<
+ GetErrorMessage(err));
+ }
+ THROW_EXCEPTION(ServerException, SocketOpenError)
+ }
+
+ mReadClosed = false;
+ mWriteClosed = false;
+ mIsServer = false; // just close the socket
+ mIsConnected = true;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::Read(void *pBuffer, int NBytes)
+// Purpose: Reads data from stream. Maybe returns less than asked for.
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
+{
+ // TODO no support for timeouts yet
+ if (Timeout != IOStream::TimeOutInfinite)
+ {
+ THROW_EXCEPTION(CommonException, AssertFailed)
+ }
+
+ if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
+
+ if (mReadClosed)
+ {
+ THROW_EXCEPTION(ConnectionException, SocketShutdownError)
+ }
+
+ // ensure safe to cast NBytes to unsigned
+ if (NBytes < 0)
+ {
+ THROW_EXCEPTION(CommonException, AssertFailed)
+ }
+
+ DWORD NumBytesRead;
+
+ if (mIsServer)
+ {
+ // satisfy from buffer if possible, to avoid
+ // blocking on read.
+ bool needAnotherRead = false;
+ if (mBytesInBuffer == 0)
+ {
+ // overlapped I/O completed successfully?
+ // (wait if needed)
+
+ if (GetOverlappedResult(mSocketHandle,
+ &mReadOverlap, &NumBytesRead, TRUE))
+ {
+ needAnotherRead = true;
+ }
+ else
+ {
+ DWORD err = GetLastError();
+
+ if (err == ERROR_HANDLE_EOF)
+ {
+ mReadClosed = true;
+ }
+ else
+ {
+ if (err == ERROR_BROKEN_PIPE)
+ {
+ BOX_ERROR("Control client "
+ "disconnected");
+ }
+ else
+ {
+ BOX_ERROR("Failed to wait for "
+ "ReadFile to complete: "
+ << GetErrorMessage(err));
+ }
+
+ Close();
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketReadError)
+ }
+ }
+ }
+ else
+ {
+ NumBytesRead = 0;
+ }
+
+ size_t BytesToCopy = NumBytesRead + mBytesInBuffer;
+ size_t BytesRemaining = 0;
+
+ if (BytesToCopy > (size_t)NBytes)
+ {
+ BytesRemaining = BytesToCopy - NBytes;
+ BytesToCopy = NBytes;
+ }
+
+ memcpy(pBuffer, mReadBuffer, BytesToCopy);
+ memmove(mReadBuffer, mReadBuffer + BytesToCopy, BytesRemaining);
+
+ mBytesInBuffer = BytesRemaining;
+ NumBytesRead = BytesToCopy;
+
+ if (needAnotherRead)
+ {
+ // reinitialise the OVERLAPPED structure
+ memset(&mReadOverlap, 0, sizeof(mReadOverlap));
+ mReadOverlap.hEvent = mReadableEvent;
+ }
+
+ // start the next overlapped read
+ if (needAnotherRead && !ReadFile(mSocketHandle,
+ mReadBuffer + mBytesInBuffer,
+ sizeof(mReadBuffer) - mBytesInBuffer,
+ NULL, &mReadOverlap))
+ {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING)
+ {
+ // Don't reset yet, there might be data
+ // in the buffer waiting to be read,
+ // will check below.
+ // ResetEvent(mReadableEvent);
+ }
+ else if (err == ERROR_HANDLE_EOF)
+ {
+ mReadClosed = true;
+ }
+ else if (err == ERROR_BROKEN_PIPE)
+ {
+ BOX_ERROR("Control client disconnected");
+ mReadClosed = true;
+ }
+ else
+ {
+ BOX_ERROR("Failed to start overlapped read: "
+ << GetErrorMessage(err));
+ Close();
+ THROW_EXCEPTION(ConnectionException,
+ 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
+ {
+ if (!ReadFile(
+ mSocketHandle, // pipe handle
+ pBuffer, // buffer to receive reply
+ NBytes, // size of buffer
+ &NumBytesRead, // number of bytes read
+ NULL)) // not overlapped
+ {
+ DWORD err = GetLastError();
+
+ Close();
+
+ // ERROR_NO_DATA is a strange name for
+ // "The pipe is being closed". No exception wanted.
+
+ if (err == ERROR_NO_DATA ||
+ err == ERROR_PIPE_NOT_CONNECTED)
+ {
+ NumBytesRead = 0;
+ }
+ else
+ {
+ BOX_ERROR("Failed to read from control socket: "
+ << GetErrorMessage(err));
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketReadError)
+ }
+ }
+
+ // Closed for reading at EOF?
+ if (NumBytesRead == 0)
+ {
+ mReadClosed = true;
+ }
+ }
+
+ return NumBytesRead;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::Write(void *pBuffer, int NBytes)
+// Purpose: Writes data, blocking until it's all done.
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void WinNamedPipeStream::Write(const void *pBuffer, int NBytes)
+{
+ if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
+
+ // Buffer in byte sized type.
+ ASSERT(sizeof(char) == 1);
+ const char *pByteBuffer = (char *)pBuffer;
+
+ int NumBytesWrittenTotal = 0;
+
+ while (NumBytesWrittenTotal < NBytes)
+ {
+ DWORD NumBytesWrittenThisTime = 0;
+
+ bool Success = WriteFile(
+ mSocketHandle, // pipe handle
+ pByteBuffer + NumBytesWrittenTotal, // message
+ NBytes - NumBytesWrittenTotal, // message length
+ &NumBytesWrittenThisTime, // bytes written this time
+ NULL); // not overlapped
+
+ 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.
+
+ if (err == ERROR_NO_DATA)
+ {
+ return;
+ }
+ else
+ {
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketWriteError)
+ }
+ }
+
+ NumBytesWrittenTotal += NumBytesWrittenThisTime;
+ }
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::Close()
+// Purpose: Closes connection to remote socket
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void WinNamedPipeStream::Close()
+{
+ if (mSocketHandle == INVALID_HANDLE_VALUE && mIsConnected)
+ {
+ BOX_ERROR("Named pipe: inconsistent connected state");
+ mIsConnected = false;
+ }
+
+ if (mSocketHandle == INVALID_HANDLE_VALUE)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
+
+ if (mIsServer)
+ {
+ if (!CancelIo(mSocketHandle))
+ {
+ BOX_ERROR("Failed to cancel outstanding I/O: " <<
+ GetErrorMessage(GetLastError()));
+ }
+
+ if (mReadableEvent == INVALID_HANDLE_VALUE)
+ {
+ BOX_ERROR("Failed to destroy Readable event: "
+ "invalid handle");
+ }
+ else if (!CloseHandle(mReadableEvent))
+ {
+ BOX_ERROR("Failed to destroy Readable event: " <<
+ GetErrorMessage(GetLastError()));
+ }
+
+ mReadableEvent = INVALID_HANDLE_VALUE;
+
+ if (!FlushFileBuffers(mSocketHandle))
+ {
+ BOX_ERROR("Failed to FlushFileBuffers: " <<
+ GetErrorMessage(GetLastError()));
+ }
+
+ if (!DisconnectNamedPipe(mSocketHandle))
+ {
+ DWORD err = GetLastError();
+ if (err != ERROR_PIPE_NOT_CONNECTED)
+ {
+ BOX_ERROR("Failed to DisconnectNamedPipe: " <<
+ GetErrorMessage(err));
+ }
+ }
+
+ mIsServer = false;
+ }
+
+ bool result = CloseHandle(mSocketHandle);
+
+ mSocketHandle = INVALID_HANDLE_VALUE;
+ mIsConnected = false;
+ mReadClosed = true;
+ mWriteClosed = true;
+
+ if (!result)
+ {
+ BOX_ERROR("Failed to CloseHandle: " <<
+ GetErrorMessage(GetLastError()));
+ THROW_EXCEPTION(ServerException, SocketCloseError)
+ }
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::StreamDataLeft()
+// Purpose: Still capable of reading data?
+// Created: 2003/08/02
+//
+// --------------------------------------------------------------------------
+bool WinNamedPipeStream::StreamDataLeft()
+{
+ return !mReadClosed;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: WinNamedPipeStream::StreamClosed()
+// Purpose: Connection been closed?
+// Created: 2003/08/02
+//
+// --------------------------------------------------------------------------
+bool WinNamedPipeStream::StreamClosed()
+{
+ return mWriteClosed;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: IOStream::WriteAllBuffered()
+// Purpose: Ensures that any data which has been buffered is written to the stream
+// Created: 2003/08/26
+//
+// --------------------------------------------------------------------------
+void WinNamedPipeStream::WriteAllBuffered()
+{
+ if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
+
+ if (!FlushFileBuffers(mSocketHandle))
+ {
+ BOX_ERROR("Failed to FlushFileBuffers: " <<
+ GetErrorMessage(GetLastError()));
+ }
+}
+
+
+#endif // WIN32
diff --git a/lib/server/WinNamedPipeStream.h b/lib/server/WinNamedPipeStream.h
new file mode 100644
index 00000000..c4ec1f3f
--- /dev/null
+++ b/lib/server/WinNamedPipeStream.h
@@ -0,0 +1,106 @@
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
+//
+// Copyright (c) 2003 - 2006
+// Ben Summers and contributors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. All use of this software and associated advertising materials must
+// display the following acknowledgment:
+// This product includes software developed by Ben Summers.
+// 4. The names of the Authors may not be used to endorse or promote
+// products derived from this software without specific prior written
+// permission.
+//
+// [Where legally impermissible the Authors do not disclaim liability for
+// direct physical injury or death caused solely by defects in the software
+// unless it is modified by a third party.]
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+//
+//
+// --------------------------------------------------------------------------
+//
+// File
+// Name: WinNamedPipeStream.h
+// Purpose: I/O stream interface for Win32 named pipes
+// Created: 2005/12/07
+//
+// --------------------------------------------------------------------------
+
+#if ! defined WINNAMEDPIPESTREAM__H && defined WIN32
+#define WINNAMEDPIPESTREAM__H
+
+#include "IOStream.h"
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: WinNamedPipeStream
+// Purpose: I/O stream interface for Win32 named pipes
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+class WinNamedPipeStream : public IOStream
+{
+public:
+ WinNamedPipeStream();
+ ~WinNamedPipeStream();
+
+ // server side - create the named pipe and listen for connections
+ void Accept(const std::string& rName);
+
+ // client side - connect to a waiting server
+ void Connect(const std::string& rName);
+
+ // both sides
+ virtual int Read(void *pBuffer, int NBytes,
+ int Timeout = IOStream::TimeOutInfinite);
+ virtual void Write(const void *pBuffer, int NBytes);
+ virtual void WriteAllBuffered();
+ 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;}
+ void MarkAsWriteClosed() {mWriteClosed = true;}
+
+private:
+ WinNamedPipeStream(const WinNamedPipeStream &rToCopy)
+ { /* do not call */ }
+
+ HANDLE mSocketHandle;
+ HANDLE mReadableEvent;
+ OVERLAPPED mReadOverlap;
+ uint8_t mReadBuffer[4096];
+ size_t mBytesInBuffer;
+ bool mReadClosed;
+ bool mWriteClosed;
+ bool mIsServer;
+ bool mIsConnected;
+
+ static std::string sPipeNamePrefix;
+};
+
+#endif // WINNAMEDPIPESTREAM__H
diff --git a/lib/server/makeprotocol.pl b/lib/server/makeprotocol.pl
index 71f2b46a..35fb3ad0 100755
--- a/lib/server/makeprotocol.pl
+++ b/lib/server/makeprotocol.pl
@@ -1,5 +1,5 @@
#!/usr/bin/perl
-# distribution boxbackup-0.10 (svn version: 494)
+# distribution boxbackup-0.11rc1 (svn version: 2023_2024)
#
# Copyright (c) 2003 - 2006
# Ben Summers and contributors. All rights reserved.
@@ -39,6 +39,9 @@
#
use strict;
+use lib "../../infrastructure";
+use BoxPlatform;
+
# Make protocol C++ classes from a protocol description file
# built in type info (values are is basic type, C++ typename)
@@ -538,11 +541,11 @@ __E
if($implement_syslog)
{
- my ($format,$args) = make_log_strings($cmd);
+ my ($log) = make_log_strings_framework($cmd);
print CPP <<__E;
void ${class}LogSysLog(const char *Action) const
{
- ::syslog(LOG_INFO,"%s $format",Action$args);
+ BOX_TRACE($log);
}
__E
}
@@ -864,8 +867,8 @@ if($implement_filelog || $implement_syslog)
}
if($implement_filelog)
{
- $fR .= qq~\tif(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain)?"Receiving stream, size uncertain":"Receiving stream, size %d\\n", Size); ::fflush(mLogToFile); }\n~;
- $fS .= qq~\tif(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain)?"Sending stream, size uncertain":"Sending stream, size %d\\n", Size); ::fflush(mLogToFile); }\n~;
+ $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~;
}
print CPP <<__E;
@@ -1019,8 +1022,15 @@ sub make_log_strings
{
# need to translate it
my ($format,$arg) = @{$log_display_types{$ty}};
- push @str,$format;
$arg =~ s/VAR/m$nm/g;
+
+ if ($format eq "0x%llx" and $target_windows)
+ {
+ $format = "0x%I64x";
+ $arg = "(uint64_t)$arg";
+ }
+
+ push @str,$format;
push @arg,$arg;
}
else
@@ -1032,4 +1042,45 @@ sub make_log_strings
return ($cmd.'('.join(',',@str).')', join(',','',@arg));
}
+sub make_log_strings_framework
+{
+ my ($cmd) = @_;
+
+ my @args;
+
+ for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
+ {
+ my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
+
+ if(exists $log_display_types{$ty})
+ {
+ # need to translate it
+ my ($format,$arg) = @{$log_display_types{$ty}};
+ $arg =~ s/VAR/m$nm/g;
+
+ if ($format =~ m'x$')
+ {
+ $arg = "std::hex << std::showbase " .
+ "<< $arg << std::dec";
+ }
+
+ push @args, $arg;
+ }
+ else
+ {
+ # is opaque
+ push @args, '"OPAQUE"';
+ }
+ }
+
+ my $log_cmd = "Action << \" $cmd(\" ";
+ foreach my $arg (@args)
+ {
+ $arg = "<< $arg ";
+ }
+ $log_cmd .= join('<< "," ',@args);
+ $log_cmd .= '<< ")"';
+ return $log_cmd;
+}
+
diff --git a/lib/server/makeprotocol.pl.in b/lib/server/makeprotocol.pl.in
new file mode 100755
index 00000000..269ff3f5
--- /dev/null
+++ b/lib/server/makeprotocol.pl.in
@@ -0,0 +1,1048 @@
+#!@PERL@
+use strict;
+
+use lib "../../infrastructure";
+use BoxPlatform;
+
+# Make protocol C++ classes from a protocol description file
+
+# built in type info (values are is basic type, C++ typename)
+# may get stuff added to it later if protocol uses extra types
+my %translate_type_info =
+(
+ 'int64' => [1, 'int64_t'],
+ 'int32' => [1, 'int32_t'],
+ 'int16' => [1, 'int16_t'],
+ 'int8' => [1, 'int8_t'],
+ 'bool' => [1, 'bool'],
+ 'string' => [0, 'std::string']
+);
+
+# built in instructions for logging various types
+# may be added to
+my %log_display_types =
+(
+ 'int64' => ['0x%llx', 'VAR'],
+ 'int32' => ['0x%x', 'VAR'],
+ 'int16' => ['0x%x', 'VAR'],
+ 'int8' => ['0x%x', 'VAR'],
+ 'bool' => ['%s', '((VAR)?"true":"false")'],
+ 'string' => ['%s', 'VAR.c_str()']
+);
+
+
+
+my ($type, $file) = @ARGV;
+
+if($type ne 'Server' && $type ne 'Client')
+{
+ die "Neither Server or Client is specified on command line\n";
+}
+
+open IN, $file or die "Can't open input file $file\n";
+
+print "Making $type protocol classes from $file...\n";
+
+my @extra_header_files;
+
+my $implement_syslog = 0;
+my $implement_filelog = 0;
+
+# read attributes
+my %attr;
+while(<IN>)
+{
+ # get and clean line
+ my $l = $_; $l =~ s/#.*\Z//; $l =~ s/\A\s+//; $l =~ s/\s+\Z//; next unless $l =~ m/\S/;
+
+ last if $l eq 'BEGIN_OBJECTS';
+
+ my ($k,$v) = split /\s+/,$l,2;
+
+ if($k eq 'ClientType')
+ {
+ add_type($v) if $type eq 'Client';
+ }
+ elsif($k eq 'ServerType')
+ {
+ add_type($v) if $type eq 'Server';
+ }
+ elsif($k eq 'ImplementLog')
+ {
+ my ($log_if_type,$log_type) = split /\s+/,$v;
+ if($type eq $log_if_type)
+ {
+ if($log_type eq 'syslog')
+ {
+ $implement_syslog = 1;
+ }
+ elsif($log_type eq 'file')
+ {
+ $implement_filelog = 1;
+ }
+ else
+ {
+ printf("ERROR: Unknown log type for implementation: $log_type\n");
+ exit(1);
+ }
+ }
+ }
+ elsif($k eq 'LogTypeToText')
+ {
+ my ($log_if_type,$type_name,$printf_format,$arg_template) = split /\s+/,$v;
+ if($type eq $log_if_type)
+ {
+ $log_display_types{$type_name} = [$printf_format,$arg_template]
+ }
+ }
+ else
+ {
+ $attr{$k} = $v;
+ }
+}
+
+sub add_type
+{
+ my ($protocol_name, $cpp_name, $header_file) = split /\s+/,$_[0];
+
+ $translate_type_info{$protocol_name} = [0, $cpp_name];
+ push @extra_header_files, $header_file;
+}
+
+# check attributes
+for(qw/Name ServerContextClass IdentString/)
+{
+ if(!exists $attr{$_})
+ {
+ die "Attribute $_ is required, but not specified\n";
+ }
+}
+
+my $protocol_name = $attr{'Name'};
+my ($context_class, $context_class_inc) = split /\s+/,$attr{'ServerContextClass'};
+my $ident_string = $attr{'IdentString'};
+
+my $current_cmd = '';
+my %cmd_contents;
+my %cmd_attributes;
+my %cmd_constants;
+my %cmd_id;
+my @cmd_list;
+
+# read in the command definitions
+while(<IN>)
+{
+ # get and clean line
+ my $l = $_; $l =~ s/#.*\Z//; $l =~ s/\s+\Z//; next unless $l =~ m/\S/;
+
+ # definitions or new command thing?
+ if($l =~ m/\A\s+/)
+ {
+ die "No command defined yet" if $current_cmd eq '';
+
+ # definition of component
+ $l =~ s/\A\s+//;
+
+ my ($type,$name,$value) = split /\s+/,$l;
+ if($type eq 'CONSTANT')
+ {
+ push @{$cmd_constants{$current_cmd}},"$name = $value"
+ }
+ else
+ {
+ push @{$cmd_contents{$current_cmd}},$type,$name;
+ }
+ }
+ else
+ {
+ # new command
+ my ($name,$id,@attributes) = split /\s+/,$l;
+ $cmd_attributes{$name} = [@attributes];
+ $cmd_id{$name} = int($id);
+ $current_cmd = $name;
+ push @cmd_list,$name;
+ }
+}
+
+close IN;
+
+
+
+# open files
+my $h_filename = 'autogen_'.$protocol_name.'Protocol'.$type.'.h';
+open CPP,'>autogen_'.$protocol_name.'Protocol'.$type.'.cpp';
+open H,">$h_filename";
+
+print CPP <<__E;
+
+// Auto-generated file -- do not edit
+
+#include "Box.h"
+#include "$h_filename"
+#include "IOStream.h"
+
+__E
+
+if($implement_syslog)
+{
+ print H <<EOF;
+#ifndef WIN32
+#include <syslog.h>
+#endif
+EOF
+}
+
+
+my $guardname = uc 'AUTOGEN_'.$protocol_name.'Protocol'.$type.'_H';
+print H <<__E;
+
+// Auto-generated file -- do not edit
+
+#ifndef $guardname
+#define $guardname
+
+#include "Protocol.h"
+#include "ProtocolObject.h"
+#include "ServerException.h"
+
+class IOStream;
+
+__E
+
+if($implement_filelog)
+{
+ print H qq~#include <stdio.h>\n~;
+}
+
+# extra headers
+for(@extra_header_files)
+{
+ print H qq~#include "$_"\n~
+}
+print H "\n";
+
+if($type eq 'Server')
+{
+ # need utils file for the server
+ print H '#include "Utils.h"',"\n\n"
+}
+
+
+my $derive_objects_from = 'ProtocolObject';
+my $objects_extra_h = '';
+my $objects_extra_cpp = '';
+if($type eq 'Server')
+{
+ # define the context
+ print H "class $context_class;\n\n";
+ print CPP "#include \"$context_class_inc\"\n\n";
+
+ # change class we derive the objects from
+ $derive_objects_from = $protocol_name.'ProtocolObject';
+
+ $objects_extra_h = <<__E;
+ virtual std::auto_ptr<ProtocolObject> DoCommand(${protocol_name}ProtocolServer &rProtocol, $context_class &rContext);
+__E
+ $objects_extra_cpp = <<__E;
+std::auto_ptr<ProtocolObject> ${derive_objects_from}::DoCommand(${protocol_name}ProtocolServer &rProtocol, $context_class &rContext)
+{
+ THROW_EXCEPTION(ConnectionException, Conn_Protocol_TriedToExecuteReplyCommand)
+}
+__E
+}
+
+print CPP qq~#include "MemLeakFindOn.h"\n~;
+
+if($type eq 'Client' && ($implement_syslog || $implement_filelog))
+{
+ # change class we derive the objects from
+ $derive_objects_from = $protocol_name.'ProtocolObjectCl';
+}
+if($implement_syslog)
+{
+ $objects_extra_h .= <<__E;
+ virtual void LogSysLog(const char *Action) const = 0;
+__E
+}
+if($implement_filelog)
+{
+ $objects_extra_h .= <<__E;
+ virtual void LogFile(const char *Action, FILE *file) const = 0;
+__E
+}
+
+if($derive_objects_from ne 'ProtocolObject')
+{
+ # output a definition for the protocol object derviced class
+ print H <<__E;
+class ${protocol_name}ProtocolServer;
+
+class $derive_objects_from : public ProtocolObject
+{
+public:
+ $derive_objects_from();
+ virtual ~$derive_objects_from();
+ $derive_objects_from(const $derive_objects_from &rToCopy);
+
+$objects_extra_h
+};
+__E
+
+ # and some cpp definitions
+ print CPP <<__E;
+${derive_objects_from}::${derive_objects_from}()
+{
+}
+${derive_objects_from}::~${derive_objects_from}()
+{
+}
+${derive_objects_from}::${derive_objects_from}(const $derive_objects_from &rToCopy)
+{
+}
+$objects_extra_cpp
+__E
+}
+
+
+
+my $classname_base = $protocol_name.'Protocol'.$type;
+
+# output the classes
+for my $cmd (@cmd_list)
+{
+ print H <<__E;
+class $classname_base$cmd : public $derive_objects_from
+{
+public:
+ $classname_base$cmd();
+ $classname_base$cmd(const $classname_base$cmd &rToCopy);
+ ~$classname_base$cmd();
+ int GetType() const;
+ enum
+ {
+ TypeID = $cmd_id{$cmd}
+ };
+__E
+ # constants
+ if(exists $cmd_constants{$cmd})
+ {
+ print H "\tenum\n\t{\n\t\t";
+ print H join(",\n\t\t",@{$cmd_constants{$cmd}});
+ print H "\n\t};\n";
+ }
+ # flags
+ if(obj_is_type($cmd,'EndsConversation'))
+ {
+ print H "\tbool IsConversationEnd() const;\n";
+ }
+ if(obj_is_type($cmd,'IsError'))
+ {
+ print H "\tbool IsError(int &rTypeOut, int &rSubTypeOut) const;\n";
+ }
+ 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"
+ }
+
+ # want to be able to read from streams?
+ my $read_from_streams = (obj_is_type($cmd,'Command') && $type eq 'Server') || (obj_is_type($cmd,'Reply') && $type eq 'Client');
+ my $write_to_streams = (obj_is_type($cmd,'Command') && $type eq 'Client') || (obj_is_type($cmd,'Reply') && $type eq 'Server');
+
+ if($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 = '';
+ if($write_to_streams)
+ {
+ # 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$classname_base$cmd(".$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";
+ }
+ }
+
+ if($implement_syslog)
+ {
+ print H "\tvirtual void LogSysLog(const char *Action) const;\n";
+ }
+ if($implement_filelog)
+ {
+ print H "\tvirtual void LogFile(const char *Action, FILE *file) const;\n";
+ }
+
+
+ # write member variables and setup for cpp file
+ my @def_constructor_list;
+ my @copy_constructor_list;
+ my @param_constructor_list;
+
+ print H "private:\n";
+ 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_member_type($ty)." m$nm;\n";
+
+ my ($basic,$typename) = translate_type($ty);
+ if($basic)
+ {
+ push @def_constructor_list, "m$nm(0)";
+ }
+ push @copy_constructor_list, "m$nm(rToCopy.m$nm)";
+ push @param_constructor_list, "m$nm($nm)";
+ }
+
+ # finish off
+ print H "};\n\n";
+
+ # now the cpp file...
+ my $def_con_vars = join(",\n\t ",@def_constructor_list);
+ $def_con_vars = "\n\t: ".$def_con_vars if $def_con_vars ne '';
+ my $copy_con_vars = join(",\n\t ",@copy_constructor_list);
+ $copy_con_vars = "\n\t: ".$copy_con_vars if $copy_con_vars ne '';
+ my $param_con_vars = join(",\n\t ",@param_constructor_list);
+ $param_con_vars = "\n\t: ".$param_con_vars if $param_con_vars ne '';
+
+ my $class = "$classname_base$cmd".'::';
+ print CPP <<__E;
+$class$classname_base$cmd()$def_con_vars
+{
+}
+$class$classname_base$cmd(const $classname_base$cmd &rToCopy)$copy_con_vars
+{
+}
+$class~$classname_base$cmd()
+{
+}
+int ${class}GetType() const
+{
+ return $cmd_id{$cmd};
+}
+__E
+ if($read_from_streams)
+ {
+ print CPP "void ${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";
+ }
+ if($write_to_streams)
+ {
+ # implement extra constructor?
+ if($param_con_vars ne '')
+ {
+ print CPP "$class$classname_base$cmd($param_con_args)$param_con_vars\n{\n}\n";
+ }
+ print CPP "void ${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 ${class}IsConversationEnd() const\n{\n\treturn true;\n}\n";
+ }
+ if(obj_is_type($cmd,'IsError'))
+ {
+ # get parameters
+ my ($mem_type,$mem_subtype) = split /,/,obj_get_type_params($cmd,'IsError');
+ print CPP <<__E;
+bool ${class}IsError(int &rTypeOut, int &rSubTypeOut) const
+{
+ rTypeOut = m$mem_type;
+ rSubTypeOut = m$mem_subtype;
+ return true;
+}
+__E
+ }
+
+ if($implement_syslog)
+ {
+ my ($log) = make_log_strings_framework($cmd);
+ print CPP <<__E;
+void ${class}LogSysLog(const char *Action) const
+{
+ BOX_TRACE($log);
+}
+__E
+ }
+ if($implement_filelog)
+ {
+ my ($format,$args) = make_log_strings($cmd);
+ print CPP <<__E;
+void ${class}LogFile(const char *Action, FILE *File) const
+{
+ ::fprintf(File,"%s $format\\n",Action$args);
+ ::fflush(File);
+}
+__E
+ }
+}
+
+# finally, the protocol object itself
+print H <<__E;
+class $classname_base : public Protocol
+{
+public:
+ $classname_base(IOStream &rStream);
+ virtual ~$classname_base();
+
+ std::auto_ptr<$derive_objects_from> Receive();
+ void Send(const ${derive_objects_from} &rObject);
+__E
+if($implement_syslog)
+{
+ print H "\tvoid SetLogToSysLog(bool Log = false) {mLogToSysLog = Log;}\n";
+}
+if($implement_filelog)
+{
+ print H "\tvoid SetLogToFile(FILE *File = 0) {mLogToFile = File;}\n";
+}
+if($type eq 'Server')
+{
+ # need to put in the conversation function
+ print H "\tvoid DoServer($context_class &rContext);\n\n";
+ # and the send vector thing
+ print H "\tvoid SendStreamAfterCommand(IOStream *pStream);\n\n";
+}
+if($type eq 'Client')
+{
+ # 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?', IOStream &rStream':'';
+ my $queryextra = $has_stream?', rStream':'';
+ my $reply = obj_get_type_params($cmd,'Command');
+ print H "\tstd::auto_ptr<$classname_base$reply> Query(const $classname_base$cmd &rQuery$argextra);\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 .= "\tinline std::auto_ptr<$classname_base$reply> Query$cmd($ar$argextra)\n\t{\n";
+ $with_params .= "\t\t$classname_base$cmd send$nar;\n";
+ $with_params .= "\t\treturn Query(send$queryextra);\n";
+ $with_params .= "\t}\n";
+ }
+ }
+ # quick hack to correct bad argument lists for commands with zero paramters but with streams
+ $with_params =~ s/\(, /(/g;
+ print H "\n",$with_params,"\n";
+}
+print H <<__E;
+private:
+ $classname_base(const $classname_base &rToCopy);
+__E
+if($type eq 'Server')
+{
+ # need to put the streams to send vector
+ print H "\tstd::vector<IOStream*> mStreamsToSend;\n\tvoid DeleteStreamsToSend();\n";
+}
+
+if($implement_filelog || $implement_syslog)
+{
+ print H <<__E;
+ virtual void InformStreamReceiving(u_int32_t Size);
+ virtual void InformStreamSending(u_int32_t Size);
+__E
+}
+
+if($implement_syslog)
+{
+ print H "private:\n\tbool mLogToSysLog;\n";
+}
+if($implement_filelog)
+{
+ print H "private:\n\tFILE *mLogToFile;\n";
+}
+print H <<__E;
+
+protected:
+ virtual std::auto_ptr<ProtocolObject> MakeProtocolObject(int ObjType);
+ virtual const char *GetIdentString();
+};
+
+__E
+
+my $construtor_extra = '';
+$construtor_extra .= ', mLogToSysLog(false)' if $implement_syslog;
+$construtor_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
+{
+}
+$prefix~$classname_base()
+{$destructor_extra
+}
+const char *${prefix}GetIdentString()
+{
+ return "$ident_string";
+}
+std::auto_ptr<ProtocolObject> ${prefix}MakeProtocolObject(int ObjType)
+{
+ switch(ObjType)
+ {
+__E
+
+# do objects within this
+for my $cmd (@cmd_list)
+{
+ print CPP <<__E;
+ case $cmd_id{$cmd}:
+ return std::auto_ptr<ProtocolObject>(new $classname_base$cmd);
+ break;
+__E
+}
+
+print CPP <<__E;
+ default:
+ THROW_EXCEPTION(ConnectionException, Conn_Protocol_UnknownCommandRecieved)
+ }
+}
+__E
+# write receieve and send functions
+print CPP <<__E;
+std::auto_ptr<$derive_objects_from> ${prefix}Receive()
+{
+ std::auto_ptr<${derive_objects_from}> preply((${derive_objects_from}*)(Protocol::Receive().release()));
+
+__E
+ if($implement_syslog)
+ {
+ print CPP <<__E;
+ if(mLogToSysLog)
+ {
+ preply->LogSysLog("Receive");
+ }
+__E
+ }
+ if($implement_filelog)
+ {
+ print CPP <<__E;
+ if(mLogToFile != 0)
+ {
+ preply->LogFile("Receive", mLogToFile);
+ }
+__E
+ }
+print CPP <<__E;
+
+ return preply;
+}
+
+void ${prefix}Send(const ${derive_objects_from} &rObject)
+{
+__E
+ if($implement_syslog)
+ {
+ print CPP <<__E;
+ if(mLogToSysLog)
+ {
+ rObject.LogSysLog("Send");
+ }
+__E
+ }
+ if($implement_filelog)
+ {
+ print CPP <<__E;
+ if(mLogToFile != 0)
+ {
+ rObject.LogFile("Send", mLogToFile);
+ }
+__E
+ }
+
+print CPP <<__E;
+ Protocol::Send(rObject);
+}
+
+__E
+# write server function?
+if($type eq 'Server')
+{
+ print CPP <<__E;
+void ${prefix}DoServer($context_class &rContext)
+{
+ // Handshake with client
+ Handshake();
+
+ // Command processing loop
+ bool inProgress = true;
+ while(inProgress)
+ {
+ // 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()));
+
+ // Send any streams
+ for(unsigned int s = 0; s < mStreamsToSend.size(); s++)
+ {
+ // Send the streams
+ SendStream(*mStreamsToSend[s]);
+ }
+ // Delete these streams
+ DeleteStreamsToSend();
+
+ // Does this end the conversation?
+ if(pobj->IsConversationEnd())
+ {
+ inProgress = false;
+ }
+ }
+}
+
+void ${prefix}SendStreamAfterCommand(IOStream *pStream)
+{
+ ASSERT(pStream != NULL);
+ mStreamsToSend.push_back(pStream);
+}
+
+void ${prefix}DeleteStreamsToSend()
+{
+ for(std::vector<IOStream*>::iterator i(mStreamsToSend.begin()); i != mStreamsToSend.end(); ++i)
+ {
+ delete (*i);
+ }
+ mStreamsToSend.clear();
+}
+
+__E
+}
+
+# write logging functions?
+if($implement_filelog || $implement_syslog)
+{
+ my ($fR,$fS);
+
+ 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~;
+ }
+ 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~;
+ }
+
+ print CPP <<__E;
+
+void ${prefix}InformStreamReceiving(u_int32_t Size)
+{
+$fR}
+
+void ${prefix}InformStreamSending(u_int32_t Size)
+{
+$fS}
+
+__E
+}
+
+
+# write client Query functions?
+if($type eq 'Client')
+{
+ for my $cmd (@cmd_list)
+ {
+ if(obj_is_type($cmd,'Command'))
+ {
+ my $reply = obj_get_type_params($cmd,'Command');
+ my $reply_id = $cmd_id{$reply};
+ my $has_stream = obj_is_type($cmd,'StreamWithCommand');
+ my $argextra = $has_stream?', IOStream &rStream':'';
+ my $send_stream_extra = '';
+ if($has_stream)
+ {
+ $send_stream_extra = <<__E;
+
+ // Send stream after the command
+ SendStream(rStream);
+__E
+ }
+ print CPP <<__E;
+std::auto_ptr<$classname_base$reply> ${classname_base}::Query(const $classname_base$cmd &rQuery$argextra)
+{
+ // Send query
+ Send(rQuery);
+ $send_stream_extra
+ // Wait for the reply
+ std::auto_ptr<${derive_objects_from}> preply(Receive().release());
+
+ if(preply->GetType() == $reply_id)
+ {
+ // Correct response
+ return std::auto_ptr<$classname_base$reply>(($classname_base$reply*)preply.release());
+ }
+ else
+ {
+ // Set protocol error
+ int type, subType;
+ if(preply->IsError(type, subType))
+ {
+ SetError(type, subType);
+ TRACE2("Protocol: Error received %d/%d\\n", type, subType);
+ }
+ else
+ {
+ SetError(Protocol::UnknownError, Protocol::UnknownError);
+ }
+
+ // Throw an exception
+ THROW_EXCEPTION(ConnectionException, Conn_Protocol_UnexpectedReply)
+ }
+}
+__E
+ }
+ }
+}
+
+
+
+print H <<__E;
+#endif // $guardname
+
+__E
+
+# close files
+close H;
+close CPP;
+
+
+sub obj_is_type
+{
+ my ($c,$ty) = @_;
+ for(@{$cmd_attributes{$c}})
+ {
+ return 1 if $_ =~ m/\A$ty/;
+ }
+
+ return 0;
+}
+
+sub obj_get_type_params
+{
+ my ($c,$ty) = @_;
+ for(@{$cmd_attributes{$c}})
+ {
+ return $1 if $_ =~ m/\A$ty\((.+?)\)\Z/;
+ }
+ die "Can't find attribute $ty\n"
+}
+
+# returns (is basic type, typename)
+sub translate_type
+{
+ my $ty = $_[0];
+
+ if($ty =~ m/\Avector\<(.+?)\>\Z/)
+ {
+ my $v_type = $1;
+ my (undef,$v_ty) = translate_type($v_type);
+ return (0, 'std::vector<'.$v_ty.'>')
+ }
+ else
+ {
+ if(!exists $translate_type_info{$ty})
+ {
+ die "Don't know about type name $ty\n";
+ }
+ return @{$translate_type_info{$ty}}
+ }
+}
+
+sub translate_type_to_arg_type
+{
+ my ($basic,$typename) = translate_type(@_);
+ return $basic?$typename:'const '.$typename.'&'
+}
+
+sub translate_type_to_member_type
+{
+ my ($basic,$typename) = translate_type(@_);
+ return $typename
+}
+
+sub make_log_strings
+{
+ my ($cmd) = @_;
+
+ my @str;
+ my @arg;
+ for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
+ {
+ my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
+
+ if(exists $log_display_types{$ty})
+ {
+ # need to translate it
+ my ($format,$arg) = @{$log_display_types{$ty}};
+ $arg =~ s/VAR/m$nm/g;
+
+ if ($format eq "0x%llx" and $target_windows)
+ {
+ $format = "0x%I64x";
+ $arg = "(uint64_t)$arg";
+ }
+
+ push @str,$format;
+ push @arg,$arg;
+ }
+ else
+ {
+ # is opaque
+ push @str,'OPAQUE';
+ }
+ }
+ return ($cmd.'('.join(',',@str).')', join(',','',@arg));
+}
+
+sub make_log_strings_framework
+{
+ my ($cmd) = @_;
+
+ my @args;
+
+ for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
+ {
+ my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
+
+ if(exists $log_display_types{$ty})
+ {
+ # need to translate it
+ my ($format,$arg) = @{$log_display_types{$ty}};
+ $arg =~ s/VAR/m$nm/g;
+
+ if ($format =~ m'x$')
+ {
+ $arg = "std::hex << std::showbase " .
+ "<< $arg << std::dec";
+ }
+
+ push @args, $arg;
+ }
+ else
+ {
+ # is opaque
+ push @args, '"OPAQUE"';
+ }
+ }
+
+ my $log_cmd = "Action << \" $cmd(\" ";
+ foreach my $arg (@args)
+ {
+ $arg = "<< $arg ";
+ }
+ $log_cmd .= join('<< "," ',@args);
+ $log_cmd .= '<< ")"';
+ return $log_cmd;
+}
+
+