summaryrefslogtreecommitdiff
path: root/lib/server/Daemon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/server/Daemon.cpp')
-rw-r--r--lib/server/Daemon.cpp450
1 files changed, 333 insertions, 117 deletions
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";
}