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.cpp328
1 files changed, 225 insertions, 103 deletions
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 <level> Set verbosity to error/warning/notice/info/trace/everything\n"
" -t <tag> 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,7 +350,8 @@ 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);
}
@@ -322,6 +359,98 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// --------------------------------------------------------------------------
//
// 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<Configuration> 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<Configuration> 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
// Name: Daemon::Main(const std::string& rConfigFileName)
// Purpose: Starts the daemon off -- equivalent of C main() function
// Created: 2003/07/29
@@ -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<Configuration> 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<Configuration> pconfig =
+ std::auto_ptr<Configuration> 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)
}