Scheduler: Implement new file descriptor event API
[senf.git] / Utils / Daemon / Daemon.cc
index f1ac715..b04eef7 100644 (file)
@@ -34,6 +34,7 @@
 #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_
@@ -82,11 +88,29 @@ prefix_ int senf::Daemon::argc()
     return argc_;
 }
 
-prefix_ char const ** senf::Daemon::argv() 
+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) {
@@ -118,6 +142,35 @@ prefix_ void senf::Daemon::openLog()
     }
 }
 
+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;
@@ -184,7 +237,7 @@ prefix_ void senf::Daemon::exit(unsigned code)
     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;
@@ -196,6 +249,7 @@ prefix_ int senf::Daemon::start(int argc, char const ** argv)
             openLog();
             fork();
         }
+        installSighandlers();
         if (! pidfile_.empty()) {
             if (pidfileCreate())
                 pidfileCreated_ = true;
@@ -228,13 +282,24 @@ prefix_ int senf::Daemon::start(int argc, char const ** argv)
     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_(""),
       pidfileCreated_(false), detached_(false)
-{}
+{
+    BOOST_ASSERT( ! instance_ );
+    instance_ = this;
+}
+
+senf::Daemon * senf::Daemon::instance_ (0);
 
 ////////////////////////////////////////
 // private members
@@ -312,6 +377,8 @@ prefix_ void senf::Daemon::fork()
         LIBC_CALL( ::close, (cerrpipe[1]) );
         LIBC_CALL( ::setsid, () );
         LIBC_CALL( ::sigprocmask, (SIG_SETMASK, &oldsig, 0) );
+
+        senf::Scheduler::instance().restart();
         return;
     }
 
@@ -321,6 +388,8 @@ prefix_ void senf::Daemon::fork()
     LIBC_CALL( ::close, (coutpipe[1]) );
     LIBC_CALL( ::close, (cerrpipe[1]) );
 
+    senf::Scheduler::instance().restart();
+
     detail::DaemonWatcher watcher (pid, coutpipe[0], cerrpipe[0], stdout_, stderr_);
     watcher.run();
 
@@ -417,6 +486,75 @@ prefix_ bool senf::Daemon::pidfileCreate()
     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
 
@@ -424,6 +562,8 @@ prefix_ senf::detail::DaemonWatcher::DaemonWatcher(int pid, int coutpipe, int ce
                                                    int stdout, int stderr)
     : childPid_(pid), coutpipe_(coutpipe), cerrpipe_(cerrpipe), stdout_(stdout),
       stderr_(stderr), sigChld_(false),
+      cldSignal_ (SIGCHLD, senf::membind(&DaemonWatcher::sigChld, this)),
+      timer_ ("DaemanWatcher watchdog", senf::membind(&DaemonWatcher::childOk, this)),
       coutForwarder_(coutpipe_, boost::bind(&DaemonWatcher::pipeClosed, this, 1)), 
       cerrForwarder_(cerrpipe_, boost::bind(&DaemonWatcher::pipeClosed, this, 2)) 
 {
@@ -437,7 +577,6 @@ prefix_ senf::detail::DaemonWatcher::DaemonWatcher(int pid, int coutpipe, int ce
 
 prefix_ void senf::detail::DaemonWatcher::run()
 {
-    Scheduler::instance().registerSignal(SIGCHLD, senf::membind(&DaemonWatcher::sigChld, this));
     Scheduler::instance().process();
 }
 
@@ -454,15 +593,13 @@ prefix_ void senf::detail::DaemonWatcher::pipeClosed(int id)
     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::instance().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)
@@ -477,10 +614,10 @@ prefix_ void senf::detail::DaemonWatcher::childDied()
         ::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));
 }
 
@@ -493,29 +630,22 @@ prefix_ void senf::detail::DaemonWatcher::childOk()
 // 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_("DaemanWatcher::Forwarder", 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);
@@ -523,17 +653,18 @@ prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(Scheduler::EventId
     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;
     }
 
@@ -542,20 +673,16 @@ prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(Scheduler::EventId
 
     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) {
         // 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;
@@ -582,7 +709,7 @@ prefix_ void senf::detail::DaemonWatcher::Forwarder::writeData(Scheduler::EventI
         i->offset -= n;
 
     if (target->offset >= buffer_.size())
-        Scheduler::instance().remove(target->fd);
+        target->writeevent.disable();
     if (src_ == -1 && (buffer_.empty() || targets_.empty()))
         cb_();
 }