#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
+#include <execinfo.h>
#include <sstream>
#include <algorithm>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/format.hpp>
#include "../Exception.hh"
#include "../membind.hh"
+#include "../Backtrace.hh"
+#include "../signalnames.hh"
+
+// #define __USE_GNU
+#include <ucontext.h>
//#include "Daemon.mpp"
#define prefix_
prefix_ senf::Daemon::~Daemon()
{
- if (! pidfile_.empty()) {
+ if (pidfileCreated_) {
try {
LIBC_CALL( ::unlink, (pidfile_.c_str()) );
} catch (Exception e) {
return daemonize_;
}
-prefix_ int senf::Daemon::argc() {
+prefix_ int senf::Daemon::argc()
+{
return argc_;
}
-prefix_ char const ** senf::Daemon::argv() {
+prefix_ char const ** senf::Daemon::argv()
+{
return argv_;
}
+namespace {
+
+ struct IsDaemonOpt {
+ bool operator()(std::string const & str) const {
+ return str == "--no-daemon"
+ || boost::starts_with(str, std::string("--pid-file="))
+ || boost::starts_with(str, std::string("--console-log="));
+ }
+ };
+}
+
+prefix_ void senf::Daemon::removeDaemonArgs()
+{
+ char const ** last (std::remove_if(argv_+1, argv_+argc_, IsDaemonOpt()));
+ *last = 0;
+ argc_ = last - argv_;
+}
+
prefix_ void senf::Daemon::consoleLog(std::string const & path, StdStream which)
{
switch (which) {
}
}
+prefix_ void senf::Daemon::logReopen()
+{
+ if (! stdoutLog_.empty()) {
+ int fd (::open(stdoutLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666));
+ if (fd < 0)
+ goto error;
+ if (::dup2(fd, 1) < 0)
+ goto error;
+ if (stderrLog_ == stdoutLog_) {
+ if (::dup2(fd, 2) < 0)
+ goto error;
+ return;
+ }
+ }
+ if (! stderrLog_.empty()) {
+ int fd (::open(stderrLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666));
+ if (fd < 0)
+ goto error;
+ if (::dup2(fd, 2) < 0)
+ goto error;
+ }
+ return;
+
+ error:
+ SENF_LOG(
+ (senf::log::CRITICAL)
+ ("log-file reopen failed: (" << errno << ") " << ::strerror(errno)) );
+}
+
prefix_ void senf::Daemon::pidFile(std::string const & f)
{
pidfile_ = f;
openLog();
fork();
}
- if (! pidfile_.empty() && ! pidfileCreate()) {
- std::cerr << "\n*** PID file '" << pidfile_ << "' creation failed. Daemon running ?"
- << std::endl;
- return 1;
+ installSighandlers();
+ if (! pidfile_.empty()) {
+ if (pidfileCreate())
+ pidfileCreated_ = true;
+ else {
+ std::cerr << "PID file '" << pidfile_
+ << "' creation failed. Daemon running ?" << std::endl;
+ return 1;
+ }
}
main();
#ifndef SENF_DEBUG
catch (std::exception & e) {
- std::cerr << "\n*** Fatal exception: " << e.what() << std::endl;
+ std::cerr << "\n*** Fatal exception: " << e.what() << "\n" << std::endl;
return 1;
}
catch (...) {
- std::cerr << "\n*** Fatal exception: (unknown)" << std::endl;
+ std::cerr << "\n*** Fatal exception: (unknown)" << "\n" << std::endl;
return 1;
}
return 0;
}
+prefix_ senf::Daemon & senf::Daemon::instance()
+{
+ BOOST_ASSERT( instance_ );
+ return *instance_;
+}
+
////////////////////////////////////////
// protected members
prefix_ senf::Daemon::Daemon()
: argc_(0), argv_(0), daemonize_(true), stdout_(-1), stderr_(-1), pidfile_(""),
- detached_(false)
-{}
+ pidfileCreated_(false), detached_(false)
+{
+ BOOST_ASSERT( ! instance_ );
+ instance_ = this;
+}
+
+senf::Daemon * senf::Daemon::instance_ (0);
////////////////////////////////////////
// private members
::sigemptyset(&cldsig);
LIBC_CALL( ::sigaddset, (&cldsig, SIGCHLD) );
LIBC_CALL( ::sigprocmask, (SIG_BLOCK, &cldsig, &oldsig) );
+
+ if (! senf::scheduler::empty() )
+ std::cerr <<
+ "\n"
+ "*** WARNING ***\n"
+ "Scheduler not empty before fork(). THIS MUST NOT HAPPEN.\n"
+ "The scheduler will be reinitialized by the fork() and lose all registrations.\n"
+ "*** WARNING ***\n"
+ "\n";
LIBC_CALL_RV( pid, ::fork, () );
LIBC_CALL( ::close, (cerrpipe[1]) );
LIBC_CALL( ::setsid, () );
LIBC_CALL( ::sigprocmask, (SIG_SETMASK, &oldsig, 0) );
+
+ senf::scheduler::restart();
return;
}
LIBC_CALL( ::close, (coutpipe[1]) );
LIBC_CALL( ::close, (cerrpipe[1]) );
+ senf::scheduler::restart();
+
detail::DaemonWatcher watcher (pid, coutpipe[0], cerrpipe[0], stdout_, stderr_);
watcher.run();
if ( ! (pidf >> old_pid)
|| old_pid < 0
|| ::kill(old_pid, 0) >= 0
- || errno == EPERM )
+ || errno == EPERM ) {
+ LIBC_CALL( ::unlink, (tempname.c_str()) );
return false;
+ }
}
// If we reach this point, the pid file exists but the process mentioned within the
return true;
}
+
+#ifdef SENF_DEBUG
+
+namespace {
+ void fatalSignalsHandler(int sig, ::siginfo_t * info, void * arg)
+ {
+ // ::ucontext_t * ucontext = static_cast<ucontext_t*>(arg);
+ std::cerr << "\n" << "Signal " << senf::signalName(sig) << '(' << sig << ')'
+ << " received\n";
+
+ if (sig == SIGSEGV)
+ std::cerr << "Invalid memory access at " << info->si_addr << "\n";
+
+ static void * entries[SENF_DEBUG_BACKTRACE_NUMCALLERS];
+ unsigned nEntries( ::backtrace(entries, SENF_DEBUG_BACKTRACE_NUMCALLERS) );
+
+ // Hack the callers address into the backtrace
+ // entries[1] = reinterpret_cast<void *>(ucontext->uc_mcontext.gregs[REG_EIP]);
+
+ std::cerr << "Backtrace:\n";
+ senf::formatBacktrace(std::cerr, entries, nEntries);
+ std::cerr << "-- \n";
+
+ if (sig != SIGUSR2) {
+ ::signal(sig, SIG_DFL);
+ ::kill(::getpid(), sig);
+ }
+ }
+
+}
+
+#endif
+
+namespace {
+ void sighupHandler(int sig)
+ {
+ senf::Daemon::instance().logReopen();
+ }
+}
+
+prefix_ void senf::Daemon::installSighandlers()
+{
+ struct ::sigaction sa;
+
+ ::sigemptyset(&sa.sa_mask);
+ sa.sa_handler = &sighupHandler;
+ sa.sa_flags = SA_RESTART;
+
+ ::sigaction(SIGHUP, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ ::sigaction(SIGPIPE, &sa, NULL);
+
+#ifdef SENF_DEBUG
+ sa.sa_sigaction = &fatalSignalsHandler;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+
+ ::sigaction(SIGILL, &sa, NULL);
+ ::sigaction(SIGTRAP, &sa, NULL);
+ ::sigaction(SIGABRT, &sa, NULL);
+ ::sigaction(SIGFPE, &sa, NULL);
+ ::sigaction(SIGBUS, &sa, NULL);
+ ::sigaction(SIGSEGV, &sa, NULL);
+ ::sigaction(SIGSTKFLT, &sa, NULL);
+ ::sigaction(SIGSYS, &sa, NULL);
+ ::sigaction(SIGUSR2, &sa, NULL);
+#endif
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::detail::DaemonWatcher
int stdout, int stderr)
: childPid_(pid), coutpipe_(coutpipe), cerrpipe_(cerrpipe), stdout_(stdout),
stderr_(stderr), sigChld_(false),
+ cldSignal_ (SIGCHLD, senf::membind(&DaemonWatcher::sigChld, this)),
+ timer_ ("senf::detail::DaemonWatcher::childOk", senf::membind(&DaemonWatcher::childOk, this)),
coutForwarder_(coutpipe_, boost::bind(&DaemonWatcher::pipeClosed, this, 1)),
cerrForwarder_(cerrpipe_, boost::bind(&DaemonWatcher::pipeClosed, this, 2))
{
prefix_ void senf::detail::DaemonWatcher::run()
{
- Scheduler::instance().registerSignal(SIGCHLD, senf::membind(&DaemonWatcher::sigChld, this));
- Scheduler::instance().process();
+ scheduler::process();
}
////////////////////////////////////////
if (coutpipe_ == -1 && cerrpipe_ == -1) {
if (sigChld_)
childDied(); // does not return
- if (::kill(childPid_, SIGUSR1) < 0)
- if (errno != ESRCH) SENF_THROW_SYSTEM_EXCEPTION("::kill()");
- Scheduler::instance().timeout(
- Scheduler::instance().eventTime() + ClockService::seconds(1),
- senf::membind(&DaemonWatcher::childOk, this));
+ if (::kill(childPid_, SIGUSR1) < 0 && errno != ESRCH)
+ SENF_THROW_SYSTEM_EXCEPTION("::kill()");
+ timer_.timeout(scheduler::eventTime() + ClockService::seconds(1));
}
}
-prefix_ void senf::detail::DaemonWatcher::sigChld()
+prefix_ void senf::detail::DaemonWatcher::sigChld(siginfo_t const &)
{
sigChld_ = true;
if (coutpipe_ == -1 && cerrpipe_ == -1)
::signal(WTERMSIG(status),SIG_DFL);
::kill(::getpid(), WTERMSIG(status));
// should not be reached
- ::_exit(1);
+ ::_exit(126);
}
if (WEXITSTATUS(status) == 0)
- ::_exit(1);
+ ::_exit(127);
::_exit(WEXITSTATUS(status));
}
prefix_ void senf::detail::DaemonWatcher::childOk()
{
- Scheduler::instance().terminate();
+ scheduler::terminate();
}
///////////////////////////////////////////////////////////////////////////
// senf::detail::DaemonWatcher::Forwarder
prefix_ senf::detail::DaemonWatcher::Forwarder::Forwarder(int src, Callback cb)
- : src_(src), cb_(cb)
-{
- Scheduler::instance().add(src_, senf::membind(&Forwarder::readData, this),
- Scheduler::EV_READ);
-}
+ : src_(src), cb_(cb),
+ readevent_("senf::detail::DaemonWatcher::Forwarder::readevent", senf::membind(&Forwarder::readData, this),
+ src_, scheduler::FdEvent::EV_READ)
+{}
prefix_ senf::detail::DaemonWatcher::Forwarder::~Forwarder()
{
- if (src_ != -1)
- Scheduler::instance().remove(src_);
-
- for (Targets::iterator i (targets_.begin()); i != targets_.end(); ++i)
- if (i->offset >= buffer_.size())
- Scheduler::instance().remove(i->fd);
+ targets_.clear_and_destroy(DestroyDelete());
}
prefix_ void senf::detail::DaemonWatcher::Forwarder::addTarget(int fd)
{
- Target target = { fd, 0 };
- targets_.push_back(target);
+ targets_.push_back(*(new Target(*this, fd)));
}
-prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(Scheduler::EventId event)
+prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(int event)
{
char buf[1024];
int n (0);
while (1) {
n = ::read(src_,buf,1024);
if (n<0) {
- if (errno != EINTR) SENF_THROW_SYSTEM_EXCEPTION("::read()");
- } else
+ if (errno != EINTR)
+ SENF_THROW_SYSTEM_EXCEPTION("::read()");
+ }
+ else
break;
}
if (n == 0) {
- // Hangup
- Scheduler::instance().remove(src_);
if (buffer_.empty())
cb_();
src_ = -1;
+ readevent_.disable();
return;
}
for (Targets::iterator i (targets_.begin()); i != targets_.end(); ++i)
if (i->offset >= buffer_.size())
- Scheduler::instance().add( i->fd,
- boost::bind(&Forwarder::writeData, this, _1, i),
- Scheduler::EV_WRITE );
+ i->writeevent.enable();
buffer_.insert(buffer_.end(), buf, buf+n);
}
-prefix_ void senf::detail::DaemonWatcher::Forwarder::writeData(Scheduler::EventId event,
- Targets::iterator target)
+prefix_ void senf::detail::DaemonWatcher::Forwarder::writeData(int event, Target * target)
{
- if (event != Scheduler::EV_WRITE) {
+ if (event != scheduler::FdEvent::EV_WRITE) {
// Broken pipe while writing data ? Not much, we can do here, we just drop the data
- Scheduler::instance().remove(target->fd);
- targets_.erase(target);
+ targets_.erase_and_destroy(Targets::current(*target),DestroyDelete());
if (targets_.empty() && src_ == -1)
cb_();
return;
i->offset -= n;
if (target->offset >= buffer_.size())
- Scheduler::instance().remove(target->fd);
+ target->writeevent.disable();
if (src_ == -1 && (buffer_.empty() || targets_.empty()))
cb_();
}