diff options
Diffstat (limited to 'lib/server/Daemon.cpp')
-rw-r--r-- | lib/server/Daemon.cpp | 450 |
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"; } |