From a84d45498bd861c9225080232948a99c2e317bb8 Mon Sep 17 00:00:00 2001 From: Reinhard Tartler Date: Thu, 2 Apr 2009 13:58:11 +0200 Subject: Import upstream version 0.11~rc3~r2491 --- lib/server/Daemon.cpp | 328 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 225 insertions(+), 103 deletions(-) (limited to 'lib/server/Daemon.cpp') diff --git a/lib/server/Daemon.cpp b/lib/server/Daemon.cpp index bdbbb433..d868774f 100644 --- a/lib/server/Daemon.cpp +++ b/lib/server/Daemon.cpp @@ -47,22 +47,21 @@ Daemon *Daemon::spDaemon = 0; // // -------------------------------------------------------------------------- Daemon::Daemon() - : mpConfiguration(NULL), - mReloadConfigWanted(false), + : mReloadConfigWanted(false), mTerminateWanted(false), + #ifdef WIN32 + mSingleProcess(true), + mRunInForeground(true), + mKeepConsoleOpenAfterFork(true), + #else mSingleProcess(false), mRunInForeground(false), mKeepConsoleOpenAfterFork(false), + #endif mHaveConfigFile(false), mAppName(DaemonName()) { - if(spDaemon != NULL) - { - THROW_EXCEPTION(ServerException, AlreadyDaemonConstructed) - } - spDaemon = this; - - // And in debug builds, we'll switch on assert failure logging to syslog + // In debug builds, switch on assert failure logging to syslog ASSERT_FAILS_TO_SYSLOG_ON // And trace goes to syslog too TRACE_TO_SYSLOG(true) @@ -78,14 +77,6 @@ Daemon::Daemon() // -------------------------------------------------------------------------- Daemon::~Daemon() { - if(mpConfiguration) - { - delete mpConfiguration; - mpConfiguration = 0; - } - - ASSERT(spDaemon == this); - spDaemon = NULL; } // -------------------------------------------------------------------------- @@ -104,9 +95,9 @@ std::string Daemon::GetOptionString() { return "c:" #ifndef WIN32 - "DFk" + "DFK" #endif - "hqvVt:T"; + "hkPqQt:TUvVW:"; } void Daemon::Usage() @@ -123,13 +114,20 @@ void Daemon::Usage() #ifndef WIN32 " -D Debugging mode, do not fork, one process only, one client only\n" " -F Do not fork into background, but fork to serve multiple clients\n" +#endif " -k Keep console open after fork, keep writing log messages to it\n" +#ifndef WIN32 + " -K Stop writing log messages to console while daemon is running\n" + " -P Show process ID (PID) in console output\n" #endif " -q Run more quietly, reduce verbosity level by one, can repeat\n" + " -Q Run at minimum verbosity, log nothing\n" " -v Run more verbosely, increase verbosity level by one, can repeat\n" - " -V Run at maximum verbosity\n" + " -V Run at maximum verbosity, log everything\n" + " -W Set verbosity to error/warning/notice/info/trace/everything\n" " -t Tag console output with specified marker\n" - " -T Timestamp console output\n"; + " -T Timestamp console output\n" + " -U Timestamp console output with microseconds\n"; } // -------------------------------------------------------------------------- @@ -166,13 +164,19 @@ int Daemon::ProcessOption(signed int option) mRunInForeground = true; } break; +#endif // !WIN32 case 'k': { mKeepConsoleOpenAfterFork = true; } break; -#endif + + case 'K': + { + mKeepConsoleOpenAfterFork = false; + } + break; case 'h': { @@ -181,6 +185,12 @@ int Daemon::ProcessOption(signed int option) } break; + case 'P': + { + Console::SetShowPID(true); + } + break; + case 'q': { if(mLogLevel == Log::NOTHING) @@ -194,6 +204,13 @@ int Daemon::ProcessOption(signed int option) } break; + case 'Q': + { + mLogLevel = Log::NOTHING; + } + break; + + case 'v': { if(mLogLevel == Log::EVERYTHING) @@ -213,9 +230,21 @@ int Daemon::ProcessOption(signed int option) } break; + case 'W': + { + mLogLevel = Logging::GetNamedLevel(optarg); + if (mLogLevel == Log::INVALID) + { + BOX_FATAL("Invalid logging level"); + return 2; + } + } + break; + case 't': { - Console::SetTag(optarg); + Logging::SetProgramName(optarg); + Console::SetShowTag(true); } break; @@ -225,6 +254,13 @@ int Daemon::ProcessOption(signed int option) } break; + case 'U': + { + Console::SetShowTime(true); + Console::SetShowTimeMicros(true); + } + break; + case '?': { BOX_FATAL("Unknown option on command line: " @@ -260,7 +296,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[]) mConfigFileName = DefaultConfigFile; mAppName = argv[0]; - #ifdef NDEBUG + #ifdef BOX_RELEASE_BUILD mLogLevel = Log::NOTICE; // need an int to do math with #else mLogLevel = Log::INFO; // need an int to do math with @@ -277,7 +313,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[]) // reset getopt, just in case anybody used it before. // unfortunately glibc and BSD differ on this point! // http://www.ussg.iu.edu/hypermail/linux/kernel/0305.3/0262.html - #if HAVE_DECL_OPTRESET == 1 + #if HAVE_DECL_OPTRESET == 1 || defined WIN32 optind = 1; optreset = 1; #elif defined __GLIBC__ @@ -314,11 +350,104 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[]) return 2; } - Logging::SetGlobalLevel((Log::Level)mLogLevel); + Logging::FilterConsole((Log::Level)mLogLevel); + Logging::FilterSyslog ((Log::Level)mLogLevel); return Main(mConfigFileName); } +// -------------------------------------------------------------------------- +// +// Function +// Name: Daemon::Configure(const std::string& rConfigFileName) +// Purpose: Loads daemon configuration. Useful when you have +// a local Daemon object and don't intend to fork() +// or call Main(). +// Created: 2008/04/19 +// +// -------------------------------------------------------------------------- + +bool Daemon::Configure(const std::string& rConfigFileName) +{ + // Load the configuration file. + std::string errors; + std::auto_ptr apConfig; + + try + { + apConfig = Configuration::LoadAndVerify(rConfigFileName, + GetConfigVerify(), errors); + } + catch(BoxException &e) + { + if(e.GetType() == CommonException::ExceptionType && + e.GetSubType() == CommonException::OSFileOpenError) + { + BOX_ERROR("Failed to open configuration file: " << + rConfigFileName); + return false; + } + + throw; + } + + // Got errors? + if(apConfig.get() == 0) + { + BOX_ERROR("Failed to load or verify configuration file"); + return false; + } + + if(!Configure(*apConfig)) + { + BOX_ERROR("Failed to verify configuration file"); + return false; + } + + // Store configuration + mConfigFileName = rConfigFileName; + mLoadedConfigModifiedTime = GetConfigFileModifiedTime(); + + return true; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Daemon::Configure(const Configuration& rConfig) +// Purpose: Loads daemon configuration. Useful when you have +// a local Daemon object and don't intend to fork() +// or call Main(). +// Created: 2008/08/12 +// +// -------------------------------------------------------------------------- + +bool Daemon::Configure(const Configuration& rConfig) +{ + std::string errors; + + // Verify() may modify the configuration, e.g. adding default values + // for required keys, so need to make a copy here + std::auto_ptr apConf(new Configuration(rConfig)); + apConf->Verify(*GetConfigVerify(), errors); + + // Got errors? + if(!errors.empty()) + { + BOX_ERROR("Configuration errors: " << errors); + return false; + } + + // Store configuration + mapConfiguration = apConf; + + // Let the derived class have a go at setting up stuff + // in the initial process + SetupInInitialProcess(); + + return true; +} + // -------------------------------------------------------------------------- // // Function @@ -331,69 +460,38 @@ int Daemon::Main(const std::string &rConfigFileName) { // Banner (optional) { - #ifndef NDEBUG - BOX_NOTICE(DaemonBanner()); - #endif + BOX_SYSLOG(Log::NOTICE, DaemonBanner()); } std::string pidFileName; - mConfigFileName = rConfigFileName; - - bool asDaemon = !mSingleProcess && !mRunInForeground; + bool asDaemon = !mSingleProcess && !mRunInForeground; try { - // Load the configuration file. - std::string errors; - std::auto_ptr pconfig; - - try - { - pconfig = Configuration::LoadAndVerify( - mConfigFileName.c_str(), - GetConfigVerify(), errors); - } - catch(BoxException &e) - { - if(e.GetType() == CommonException::ExceptionType && - e.GetSubType() == CommonException::OSFileOpenError) - { - BOX_FATAL("Failed to start: failed to open " - "configuration file: " - << mConfigFileName); - return 1; - } - - throw; - } - - // Got errors? - if(pconfig.get() == 0 || !errors.empty()) + if (!Configure(rConfigFileName)) { - // Tell user about errors - BOX_FATAL("Failed to start: errors in configuration " - "file: " << mConfigFileName << ": " << errors); - // And give up + BOX_FATAL("Failed to start: failed to load " + "configuration file: " << rConfigFileName); return 1; } - // Store configuration - mpConfiguration = pconfig.release(); - mLoadedConfigModifiedTime = GetConfigFileModifiedTime(); - - // Let the derived class have a go at setting up stuff in the initial process - SetupInInitialProcess(); - // Server configuration const Configuration &serverConfig( - mpConfiguration->GetSubConfiguration("Server")); + mapConfiguration->GetSubConfiguration("Server")); + + if(serverConfig.KeyExists("LogFacility")) + { + std::string facility = + serverConfig.GetKeyValue("LogFacility"); + Logging::SetFacility(Syslog::GetNamedFacility(facility)); + } // Open PID file for writing pidFileName = serverConfig.GetKeyValue("PidFile"); FileHandleGuard<(O_WRONLY | O_CREAT | O_TRUNC), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)> pidFile(pidFileName.c_str()); -#ifndef WIN32 +#ifndef WIN32 // Handle changing to a different user if(serverConfig.KeyExists("User")) { @@ -409,7 +507,7 @@ int Daemon::Main(const std::string &rConfigFileName) // Change the process ID daemonUser.ChangeProcessUser(); } - + if(asDaemon) { // Let's go... Daemonise... @@ -436,8 +534,7 @@ int Daemon::Main(const std::string &rConfigFileName) // Set new session if(::setsid() == -1) { - BOX_ERROR("Failed to setsid(): " << - strerror(errno)); + BOX_LOG_SYS_ERROR("Failed to setsid()"); THROW_EXCEPTION(ServerException, DaemoniseFailed) } @@ -446,6 +543,7 @@ int Daemon::Main(const std::string &rConfigFileName) { case -1: // error + BOX_LOG_SYS_ERROR("Failed to fork() a child"); THROW_EXCEPTION(ServerException, DaemoniseFailed) break; @@ -460,7 +558,17 @@ int Daemon::Main(const std::string &rConfigFileName) break; } } - +#endif // !WIN32 + + // Must set spDaemon before installing signal handler, + // otherwise the handler will crash if invoked too soon. + if(spDaemon != NULL) + { + THROW_EXCEPTION(ServerException, AlreadyDaemonConstructed) + } + spDaemon = this; + +#ifndef WIN32 // Set signal handler // Don't do this in the parent, since it might be anything // (e.g. test/bbackupd) @@ -468,29 +576,24 @@ int Daemon::Main(const std::string &rConfigFileName) struct sigaction sa; sa.sa_handler = SignalHandler; sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); // macro - if(::sigaction(SIGHUP, &sa, NULL) != 0 || ::sigaction(SIGTERM, &sa, NULL) != 0) + sigemptyset(&sa.sa_mask); // macro + if(::sigaction(SIGHUP, &sa, NULL) != 0 || + ::sigaction(SIGTERM, &sa, NULL) != 0) { + BOX_LOG_SYS_ERROR("Failed to set signal handlers"); THROW_EXCEPTION(ServerException, DaemoniseFailed) } #endif // !WIN32 - // Log the start message - BOX_NOTICE("Starting daemon, version " << BOX_VERSION - << ", config: " << mConfigFileName); - // Write PID to file char pid[32]; -#ifdef WIN32 - int pidsize = sprintf(pid, "%d", (int)GetCurrentProcessId()); -#else int pidsize = sprintf(pid, "%d", (int)getpid()); -#endif if(::write(pidFile, pid, pidsize) != pidsize) { - BOX_FATAL("can't write pid file"); + BOX_LOG_SYS_FATAL("Failed to write PID file: " << + pidFileName); THROW_EXCEPTION(ServerException, DaemoniseFailed) } @@ -515,6 +618,7 @@ int Daemon::Main(const std::string &rConfigFileName) int devnull = ::open(PLATFORM_DEV_NULL, O_RDWR, 0); if(devnull == -1) { + BOX_LOG_SYS_ERROR("Failed to open /dev/null"); THROW_EXCEPTION(CommonException, OSFileError); } // Then duplicate them to all three handles @@ -530,9 +634,13 @@ int Daemon::Main(const std::string &rConfigFileName) // And definitely don't try and send anything to those file descriptors // -- this has in the past sent text to something which isn't expecting it. TRACE_TO_STDOUT(false); - Logging::ToConsole(false); #endif // ! WIN32 - } + Logging::ToConsole(false); + } + + // Log the start message + BOX_NOTICE("Starting daemon, version: " << BOX_VERSION); + BOX_NOTICE("Using configuration file: " << mConfigFileName); } catch(BoxException &e) { @@ -581,10 +689,10 @@ int Daemon::Main(const std::string &rConfigFileName) BOX_NOTICE("Reloading configuration file: " << mConfigFileName); std::string errors; - std::auto_ptr pconfig = + std::auto_ptr pconfig( Configuration::LoadAndVerify( mConfigFileName.c_str(), - GetConfigVerify(), errors); + GetConfigVerify(), errors)); // Got errors? if(pconfig.get() == 0 || !errors.empty()) @@ -598,12 +706,8 @@ int Daemon::Main(const std::string &rConfigFileName) break; } - // delete old configuration - delete mpConfiguration; - mpConfiguration = 0; - // Store configuration - mpConfiguration = pconfig.release(); + mapConfiguration = pconfig; mLoadedConfigModifiedTime = GetConfigFileModifiedTime(); @@ -638,8 +742,21 @@ int Daemon::Main(const std::string &rConfigFileName) #ifdef WIN32 WSACleanup(); +#else + // Should clean up here, but it breaks memory leak tests. + /* + if(asDaemon) + { + // we are running in the child by now, and should not return + mapConfiguration.reset(); + exit(0); + } + */ #endif - + + ASSERT(spDaemon == this); + spDaemon = NULL; + return retcode; } @@ -647,7 +764,8 @@ int Daemon::Main(const std::string &rConfigFileName) // // Function // Name: Daemon::EnterChild() -// Purpose: Sets up for a child task of the main server. Call just after fork() +// Purpose: Sets up for a child task of the main server. Call +// just after fork(). // Created: 2003/07/31 // // -------------------------------------------------------------------------- @@ -789,13 +907,13 @@ const ConfigurationVerify *Daemon::GetConfigVerify() const // -------------------------------------------------------------------------- const Configuration &Daemon::GetConfiguration() const { - if(mpConfiguration == 0) + if(mapConfiguration.get() == 0) { // Shouldn't get anywhere near this if a configuration file can't be loaded THROW_EXCEPTION(ServerException, Internal) } - return *mpConfiguration; + return *mapConfiguration; } @@ -803,8 +921,10 @@ const Configuration &Daemon::GetConfiguration() const // // Function // Name: Daemon::SetupInInitialProcess() -// Purpose: A chance for the daemon to do something initial setting up in the process which -// initiates everything, and after the configuration file has been read and verified. +// Purpose: A chance for the daemon to do something initial +// setting up in the process which initiates +// everything, and after the configuration file has +// been read and verified. // Created: 2003/08/20 // // -------------------------------------------------------------------------- @@ -849,14 +969,16 @@ void Daemon::SetProcessTitle(const char *format, ...) box_time_t Daemon::GetConfigFileModifiedTime() const { - struct stat st; + EMU_STRUCT_STAT st; - if(::stat(GetConfigFileName().c_str(), &st) != 0) + if(EMU_STAT(GetConfigFileName().c_str(), &st) != 0) { if (errno == ENOENT) { return 0; } + BOX_LOG_SYS_ERROR("Failed to stat configuration file: " << + GetConfigFileName()); THROW_EXCEPTION(CommonException, OSFileError) } -- cgit v1.2.3