// distribution boxbackup-0.10 (svn version: 494)
//
// 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: Daemon.cpp
// Purpose: Basic daemon functionality
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
#include "Box.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdarg.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#include "Daemon.h"
#include "Configuration.h"
#include "ServerException.h"
#include "Guards.h"
#include "UnixUser.h"
#include "FileModificationTime.h"
#include "MemLeakFindOn.h"
Daemon *Daemon::spDaemon = 0;
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::Daemon()
// Purpose: Constructor
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
Daemon::Daemon()
: mpConfiguration(0),
mReloadConfigWanted(false),
mTerminateWanted(false)
{
if(spDaemon != 0)
{
THROW_EXCEPTION(ServerException, AlreadyDaemonConstructed)
}
spDaemon = this;
// And in debug builds, we'll switch on assert failure logging to syslog
ASSERT_FAILS_TO_SYSLOG_ON
// And trace goes to syslog too
TRACE_TO_SYSLOG(true)
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::~Daemon()
// Purpose: Destructor
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
Daemon::~Daemon()
{
if(mpConfiguration)
{
delete mpConfiguration;
mpConfiguration = 0;
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::Main(const char *, int, const char *[])
// Purpose: Starts the daemon off -- equivalent of C main() function
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
{
// Banner (optional)
{
const char *banner = DaemonBanner();
if(banner != 0)
{
printf("%s", banner);
}
}
std::string pidFileName;
try
{
// Find filename of config file
mConfigFileName = DefaultConfigFile;
if(argc >= 2)
{
// 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
{
mConfigFileName = argv[1];
}
}
// Test mode with no daemonisation?
bool asDaemon = true;
if(argc >= 3)
{
if(::strcmp(argv[2], "SINGLEPROCESS") == 0)
{
asDaemon = false;
}
}
// 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)
{
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
return 1;
}
throw;
}
// Got errors?
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
// And give up
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();
#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"));
// 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());
// Handle changing to a different user
if(serverConfig.KeyExists("User"))
{
// Config file specifies an user -- look up
UnixUser daemonUser(serverConfig.GetKeyValue("User").c_str());
// Change the owner on the PID file, so it can be deleted properly on termination
if(::fchown(pidFile, daemonUser.GetUID(), daemonUser.GetGID()) != 0)
{
THROW_EXCEPTION(ServerException, CouldNotChangePIDFileOwner)
}
// Change the process ID
daemonUser.ChangeProcessUser();
}
if(asDaemon)
{
// Let's go... Daemonise...
switch(::fork())
{
case -1:
// error
THROW_EXCEPTION(ServerException, DaemoniseFailed)
break;
default:
// parent
_exit(0);
return 0;
break;
case 0:
// child
break;
}
// In child
// Set new session
if(::setsid() == -1)
{
::syslog(LOG_ERR, "can't setsid");
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
// Fork again...
switch(::fork())
{
case -1:
// error
THROW_EXCEPTION(ServerException, DaemoniseFailed)
break;
default:
// parent
_exit(0);
return 0;
break;
case 0:
// child
break;
}
}
#endif // ! WIN32
// open the log
::openlog(DaemonName(), LOG_PID, LOG_LOCAL6);
// Log the start message
::syslog(LOG_INFO, "Starting daemon (config: %s) (version "
BOX_VERSION ")", mConfigFileName.c_str());
#ifndef WIN32
// Write PID to file
char pid[32];
int pidsize = sprintf(pid, "%d", (int)getpid());
if(::write(pidFile, pid, pidsize) != pidsize)
{
::syslog(LOG_ERR, "can't write pid file");
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
#endif
// Set up memory leak reporting
#ifdef BOX_MEMORY_LEAK_TESTING
{
char filename[256];
sprintf(filename, "%s.memleaks", DaemonName());
memleakfinder_setup_exit_report(filename, DaemonName());
}
#endif // BOX_MEMORY_LEAK_TESTING
if(asDaemon)
{
#ifndef WIN32
// Close standard streams
::close(0);
::close(1);
::close(2);
// Open and redirect them into /dev/null
int devnull = ::open(PLATFORM_DEV_NULL, O_RDWR, 0);
if(devnull == -1)
{
THROW_EXCEPTION(CommonException, OSFileError);
}
// Then duplicate them to all three handles
if(devnull != 0) dup2(devnull, 0);
if(devnull != 1) dup2(devnull, 1);
if(devnull != 2) dup2(devnull, 2);
// Close the original handle if it was opened above the std* range
if(devnull > 2)
{
::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);
}
}
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
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
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
return 1;
}
// Main Daemon running
try
{
while(!mTerminateWanted)
{
Run();
if(mReloadConfigWanted && !mTerminateWanted)
{
// Need to reload that config file...
::syslog(LOG_INFO, "Reloading configuration "
"(config: %s)",
mConfigFileName.c_str());
std::string errors;
std::auto_ptr<Configuration> pconfig =
Configuration::LoadAndVerify(
mConfigFileName.c_str(),
GetConfigVerify(), errors);
// Got errors?
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());
// And give up
return 1;
}
// delete old configuration
delete mpConfiguration;
mpConfiguration = 0;
// Store configuration
mpConfiguration = pconfig.release();
mLoadedConfigModifiedTime =
GetConfigFileModifiedTime();
// Stop being marked for loading config again
mReloadConfigWanted = false;
}
}
// Delete the PID file
::unlink(pidFileName.c_str());
// Log
::syslog(LOG_INFO, "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;
}
catch(std::exception &e)
{
::syslog(LOG_ERR, "%s: terminating due to exception %s",
DaemonName(), e.what());
return 1;
}
catch(...)
{
::syslog(LOG_ERR, "%s: terminating due to unknown exception",
DaemonName());
return 1;
}
return 0;
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::EnterChild()
// Purpose: Sets up for a child task of the main server. Call just after fork()
// Created: 2003/07/31
//
// --------------------------------------------------------------------------
void Daemon::EnterChild()
{
#ifndef WIN32
// Unset signal handlers
struct sigaction sa;
sa.sa_handler = SIG_DFL;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask); // macro
::sigaction(SIGHUP, &sa, NULL);
::sigaction(SIGTERM, &sa, NULL);
#endif
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::SignalHandler(int)
// Purpose: Signal handler
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
void Daemon::SignalHandler(int sigraised)
{
#ifndef WIN32
if(spDaemon != 0)
{
switch(sigraised)
{
case SIGHUP:
spDaemon->mReloadConfigWanted = true;
break;
case SIGTERM:
spDaemon->mTerminateWanted = true;
break;
default:
break;
}
}
#endif
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::DaemonName()
// Purpose: Returns name of the daemon
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
const char *Daemon::DaemonName() const
{
return "generic-daemon";
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::DaemonBanner()
// Purpose: Returns the text banner for this daemon's startup
// Created: 1/1/04
//
// --------------------------------------------------------------------------
const char *Daemon::DaemonBanner() const
{
return 0;
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::Run()
// Purpose: Main run function after basic Daemon initialisation
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
void Daemon::Run()
{
while(!StopRun())
{
::sleep(10);
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::GetConfigVerify()
// Purpose: Returns the configuration file verification structure for this daemon
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
const ConfigurationVerify *Daemon::GetConfigVerify() const
{
static ConfigurationVerifyKey verifyserverkeys[] =
{
DAEMON_VERIFY_SERVER_KEYS
};
static ConfigurationVerify verifyserver[] =
{
{
"Server",
0,
verifyserverkeys,
ConfigTest_Exists | ConfigTest_LastEntry,
0
}
};
static ConfigurationVerify verify =
{
"root",
verifyserver,
0,
ConfigTest_Exists | ConfigTest_LastEntry,
0
};
return &verify;
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::GetConfiguration()
// Purpose: Returns the daemon configuration object
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
const Configuration &Daemon::GetConfiguration() const
{
if(mpConfiguration == 0)
{
// Shouldn't get anywhere near this if a configuration file can't be loaded
THROW_EXCEPTION(ServerException, Internal)
}
return *mpConfiguration;
}
// --------------------------------------------------------------------------
//
// 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.
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
void Daemon::SetupInInitialProcess()
{
// Base class doesn't do anything.
}
void Daemon::SetProcessTitle(const char *format, ...)
{
// On OpenBSD, setproctitle() sets the process title to imagename: <text> (imagename)
// -- make sure other platforms include the image name somewhere so ps listings give
// useful information.
#ifdef HAVE_SETPROCTITLE
// optional arguments
va_list args;
va_start(args, format);
// Make the string
char title[256];
::vsnprintf(title, sizeof(title), format, args);
// Set process title
::setproctitle("%s", title);
#endif // HAVE_SETPROCTITLE
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::GetConfigFileModifiedTime()
// Purpose: Returns the timestamp when the configuration file
// was last modified
//
// Created: 2006/01/29
//
// --------------------------------------------------------------------------
box_time_t Daemon::GetConfigFileModifiedTime() const
{
struct stat st;
if(::stat(GetConfigFileName().c_str(), &st) != 0)
{
if (errno == ENOENT)
{
return 0;
}
THROW_EXCEPTION(CommonException, OSFileError)
}
return FileModificationTime(st);
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::GetLoadedConfigModifiedTime()
// Purpose: Returns the timestamp when the configuration file
// had been last modified, at the time when it was
// loaded
//
// Created: 2006/01/29
//
// --------------------------------------------------------------------------
box_time_t Daemon::GetLoadedConfigModifiedTime() const
{
return mLoadedConfigModifiedTime;
}
syntax highlighted by Code2HTML, v. 0.9.1