#include <fcntl.h>
#include <errno.h>
#include <signal.h>
+#include <execinfo.h>
#include <sstream>
#include <algorithm>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
+#include <boost/format.hpp>
#include "../Exception.hh"
#include "../membind.hh"
+#include "../Backtrace.hh"
+
+// #define __USE_GNU
+#include <ucontext.h>
//#include "Daemon.mpp"
#define prefix_
///////////////////////////////cc.p////////////////////////////////////////
-#define LIBC_CALL(fn, args) if (fn args < 0) throw SystemException(#fn "()")
-#define LIBC_CALL_RV(var, fn, args) int var (fn args); if (var < 0) throw SystemException(#fn "()")
+#define LIBC_CALL(fn, args) if (fn args < 0) \
+ SENF_THROW_SYSTEM_EXCEPTION(#fn "()")
+
+#define LIBC_CALL_RV(var, fn, args) \
+ int var (fn args); if (var < 0) SENF_THROW_SYSTEM_EXCEPTION(#fn "()")
///////////////////////////////////////////////////////////////////////////
// senf::Daemon
prefix_ senf::Daemon::~Daemon()
{
- if (! pidfile_.empty())
- LIBC_CALL( ::unlink, (pidfile_.c_str()) );
+ if (pidfileCreated_) {
+ try {
+ LIBC_CALL( ::unlink, (pidfile_.c_str()) );
+ } catch (Exception e) {
+ // e << "; could not unlink " << pidfile_.c_str();
+ // throw;
+ }
+ }
}
prefix_ void senf::Daemon::daemonize(bool v)
return daemonize_;
}
+prefix_ int senf::Daemon::argc()
+{
+ return argc_;
+}
+
+prefix_ char ** 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 ** 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) {
if (! stdoutLog_.empty()) {
fd = ::open(stdoutLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666);
if (fd < 0)
- throw SystemException("::open()");
+ SENF_THROW_SYSTEM_EXCEPTION("")
+ << " Could not open \"" << stdoutLog_ << "\" for redirecting stdout.";
stdout_ = fd;
}
if (stderrLog_ == stdoutLog_)
else if (! stderrLog_.empty()) {
fd = ::open(stdoutLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666);
if (fd < 0)
- throw SystemException("::open()");
+ SENF_THROW_SYSTEM_EXCEPTION("")
+ << " Could not open \"" << stderrLog_ << "\" for redirecting stderr.";
stderr_ = fd;
}
}
+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;
while (! signaled) {
::sigsuspend(&waitsig);
if (errno != EINTR)
- throw SystemException("::sigsuspend()");
+ SENF_THROW_SYSTEM_EXCEPTION("::sigsuspend()");
}
LIBC_CALL( ::sigaction, (SIGUSR1, &oldact, 0) );
throw DaemonExitException(code);
}
-prefix_ int senf::Daemon::start(int argc, char const ** argv)
+prefix_ int senf::Daemon::start(int argc, char ** argv)
{
argc_ = argc;
argv_ = argv;
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();
return e.code;
}
-#ifdef SENF_NO_DEBUG
+#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
// was some race condition, probably over NFS.
std::string tempname;
+ boost::format linkErrorFormat(" Could not link \"%1%\" to \"%2%\".");
{
char hostname[HOST_NAME_MAX+1];
while (1) {
{
std::ofstream pidf (tempname.c_str());
+ if (! pidf)
+ SENF_THROW_SYSTEM_EXCEPTION("")
+ << " Could not open pidfile \"" << tempname << "\" for output.";
pidf << ::getpid() << std::endl;
+ if (! pidf)
+ SENF_THROW_SYSTEM_EXCEPTION("")
+ << " Could not write to pidfile \"" << tempname << "\".";
}
if (::link(tempname.c_str(), pidfile_.c_str()) < 0) {
if (errno != EEXIST)
- throw SystemException("::link()");
+ SENF_THROW_SYSTEM_EXCEPTION("") << linkErrorFormat % pidfile_ % tempname;
}
else {
struct ::stat s;
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
LIBC_CALL( ::unlink, (tempname.c_str() ));
if (::link(pidfile_.c_str(), tempname.c_str()) < 0) {
- if (errno != ENOENT) throw SystemException("::link()");
+ if (errno != ENOENT)
+ SENF_THROW_SYSTEM_EXCEPTION("") << linkErrorFormat % tempname % pidfile_;
// Hmm ... the pidfile mysteriously disappeared ... try again.
continue;
}
return true;
}
+
+#ifdef SENF_DEBUG
+
+namespace {
+ void fatalSignalsHandler(int sig, ::siginfo_t * info, void * arg)
+ {
+ static char const * const signames[] = {
+ "",
+ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE",
+ "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM",
+ "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU",
+ "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
+ "SIGPWR", "SIGSYS" };
+
+ // ::ucontext_t * ucontext = static_cast<ucontext_t*>(arg);
+ std::cerr << "\n" << "Signal " << sig;
+ if (unsigned(sig) < sizeof(signames) / sizeof(signames[0]))
+ std::cerr << " (" << signames[unsigned(sig)] << ")";
+ std::cerr << " 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);
+
+#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
if (sigChld_)
childDied(); // does not return
if (::kill(childPid_, SIGUSR1) < 0)
- if (errno != ESRCH) throw SystemException("::kill()");
+ if (errno != ESRCH) SENF_THROW_SYSTEM_EXCEPTION("::kill()");
Scheduler::instance().timeout(
Scheduler::instance().eventTime() + ClockService::seconds(1),
senf::membind(&DaemonWatcher::childOk, this));
prefix_ void senf::detail::DaemonWatcher::childDied()
{
int status (0);
- if (::waitpid(childPid_,&status,0) < 0) throw SystemException("::waitpid()");
+ if (::waitpid(childPid_,&status,0) < 0) SENF_THROW_SYSTEM_EXCEPTION("::waitpid()");
if (WIFSIGNALED(status)) {
::signal(WTERMSIG(status),SIG_DFL);
::kill(::getpid(), WTERMSIG(status));
while (1) {
n = ::read(src_,buf,1024);
if (n<0) {
- if (errno != EINTR) throw SystemException("::read()");
+ if (errno != EINTR) SENF_THROW_SYSTEM_EXCEPTION("::read()");
} else
break;
}
int w (::write(target->fd, buf, n));
if (w < 0) {
- if (errno != EINTR) throw SystemException("::write()");
+ if (errno != EINTR) SENF_THROW_SYSTEM_EXCEPTION("::write()");
return;
}
target->offset += w;