Scheduler: Implement new file descriptor event API
[senf.git] / Utils / Daemon / Daemon.cc
index 2cba370..b04eef7 100644 (file)
@@ -1,8 +1,8 @@
 // $Id$
 //
-// Copyright (C) 2007 
-// Fraunhofer Institute for Open Communication Systems (FOKUS) 
-// Competence Center NETwork research (NET), St. Augustin, GERMANY 
+// Copyright (C) 2007
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
 //     Stefan Bund <g0dil@berlios.de>
 //
 // This program is free software; you can redistribute it and/or modify
 #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"
+#include "../signalnames.hh"
+
+// #define __USE_GNU
+#include <ucontext.h>
 
 //#include "Daemon.mpp"
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
-#define LIBC_CALL(fn, args) if (fn args < 0) throwErrno(#fn "()")
-#define LIBC_CALL_RV(var, fn, args) int var (fn args); if (var < 0) throwErrno(#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)
@@ -67,6 +83,34 @@ prefix_ bool senf::Daemon::daemon()
     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) {
@@ -83,7 +127,8 @@ prefix_ void senf::Daemon::openLog()
     if (! stdoutLog_.empty()) {
         fd = ::open(stdoutLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666);
         if (fd < 0)
-            throwErrno("::open()");
+            SENF_THROW_SYSTEM_EXCEPTION("")
+                  << " Could not open \"" << stdoutLog_ << "\" for redirecting stdout.";
         stdout_ = fd;
     }
     if (stderrLog_ == stdoutLog_)
@@ -91,11 +136,41 @@ prefix_ void senf::Daemon::openLog()
     else if (! stderrLog_.empty()) {
         fd = ::open(stdoutLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666);
         if (fd < 0)
-            throwErrno("::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;
@@ -139,7 +214,7 @@ prefix_ void senf::Daemon::detach()
         while (! signaled) {
             ::sigsuspend(&waitsig);
             if (errno != EINTR)
-                throwErrno("::sigsuspend()");
+                SENF_THROW_SYSTEM_EXCEPTION("::sigsuspend()");
         }
 
         LIBC_CALL( ::sigaction, (SIGUSR1, &oldact, 0) );
@@ -162,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;
@@ -174,10 +249,15 @@ prefix_ int senf::Daemon::start(int argc, char const ** 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();
@@ -186,14 +266,14 @@ prefix_ int senf::Daemon::start(int argc, char const ** argv)
         return e.code;
     }
 
-#ifdef NDEBUG
+#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;
     }
 
@@ -202,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_(""),
-      detached_(false)
-{}
+      pidfileCreated_(false), detached_(false)
+{
+    BOOST_ASSERT( ! instance_ );
+    instance_ = this;
+}
+
+senf::Daemon * senf::Daemon::instance_ (0);
 
 ////////////////////////////////////////
 // private members
@@ -286,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;
     }
 
@@ -295,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();
 
@@ -308,6 +403,7 @@ prefix_ bool senf::Daemon::pidfileCreate()
     // 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];
@@ -321,12 +417,18 @@ prefix_ bool senf::Daemon::pidfileCreate()
     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) 
-                throwErrno("::link()");
+                SENF_THROW_SYSTEM_EXCEPTION("") << linkErrorFormat % pidfile_ % tempname;
         }
         else {
             struct ::stat s;
@@ -342,8 +444,10 @@ prefix_ bool senf::Daemon::pidfileCreate()
             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
@@ -356,7 +460,8 @@ prefix_ bool senf::Daemon::pidfileCreate()
 
         LIBC_CALL( ::unlink, (tempname.c_str() ));
         if (::link(pidfile_.c_str(), tempname.c_str()) < 0) {
-            if (errno != ENOENT) throwErrno("::link()");
+            if (errno != ENOENT)
+                SENF_THROW_SYSTEM_EXCEPTION("") << linkErrorFormat % tempname % pidfile_;
             // Hmm ... the pidfile mysteriously disappeared ... try again.
             continue;
         }
@@ -381,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
 
@@ -388,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)) 
 {
@@ -401,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();
 }
 
@@ -418,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) throwErrno("::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)
@@ -436,15 +609,15 @@ prefix_ void senf::detail::DaemonWatcher::sigChld()
 prefix_ void senf::detail::DaemonWatcher::childDied()
 {
     int status (0);
-    if (::waitpid(childPid_,&status,0) < 0) throwErrno("::waitpid()");
+    if (::waitpid(childPid_,&status,0) < 0) SENF_THROW_SYSTEM_EXCEPTION("::waitpid()");
     if (WIFSIGNALED(status)) {
         ::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));
 }
 
@@ -457,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);
@@ -487,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) throwErrno("::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;
     }
 
@@ -506,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;
@@ -531,7 +694,7 @@ prefix_ void senf::detail::DaemonWatcher::Forwarder::writeData(Scheduler::EventI
 
     int w (::write(target->fd, buf, n));
     if (w < 0) {
-        if (errno != EINTR) throwErrno("::write()");
+        if (errno != EINTR) SENF_THROW_SYSTEM_EXCEPTION("::write()");
         return;
     }
     target->offset += w;
@@ -546,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_();
 }