Packets: Fix min_value / max_value boundary cases for Parse_(U)IntField
g0dil [Fri, 9 Nov 2007 11:49:53 +0000 (11:49 +0000)]
Scheduler: Add debug log message, fix signal handlin bug
Scheduler: Remove all registered handlers/callbacks/descriptors at end of unit test
Utils/Logger: Undef SENF_LOG_CONF if defined in main.test.hh
Scheduler: Moved Daemon class from Utils here. Can't be in Utils since it depends on the Scheduler :-(
Scheduler: Daemon class: Implemented correkt fork() and simple log-passing behaviour

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@501 270642c3-0616-0410-b53a-bc976706d245

14 files changed:
Packets/ParseInt.hh
Scheduler/Daemon.cc [new file with mode: 0644]
Scheduler/Daemon.hh [new file with mode: 0644]
Scheduler/Daemon.ih [new file with mode: 0644]
Scheduler/Daemon.test.cc [new file with mode: 0644]
Scheduler/Scheduler.cc
Scheduler/Scheduler.hh
Scheduler/Scheduler.test.cc
Utils/DaemonTools.cc
Utils/DaemonTools.hh
Utils/Logger/Parameters.ih
Utils/Logger/Target.cci
Utils/Logger/Target.ih
Utils/Logger/main.test.hh

index 367bb1b..32ae476 100644 (file)
@@ -284,15 +284,6 @@ namespace senf {
     inline std::ostream & operator<<(std::ostream & os, Parse_UInt32 const & i)
     { os << i.value(); return os; }
 
-    template <int X, int Y>
-    struct ctime_pow {
-        static const int result = X * ctime_pow<X,Y-1>::result;
-    };
-    template<int X>
-    struct ctime_pow<X,0> {
-        static const int result = 1;
-    };
-    
     /** \brief Parse signed bitfield with up to 32bit's
         
         This parser will parse a bitfield beginning at the bit \a Start and ending \e before \a
@@ -328,8 +319,8 @@ namespace senf {
         static size_type const start_bit = Start;
         static size_type const end_bit = End;
         static size_type const fixed_bytes = (End-1)/8+1;
-        static value_type const min_value = -ctime_pow<2,(End-Start)-1>::result;
-        static value_type const max_value =  ctime_pow<2,(End-Start)-1>::result - 1;
+        static value_type const max_value = boost::low_bits_mask_t<End-Start-1>::sig_bits;
+        static value_type const min_value = - max_value - 1;
 
 
         value_type value() const {
@@ -386,8 +377,8 @@ namespace senf {
         static size_type const start_bit = Start;
         static size_type const end_bit = End;
         static size_type const fixed_bytes = (End-1)/8+1;
-        static value_type const min_value = 0;
-        static value_type const max_value = ctime_pow<2,(End-Start)>::result - 1;
+        static value_type const min_value = 0u;
+        static value_type const max_value = boost::low_bits_mask_t<End-Start>::sig_bits;
 
         value_type value() const { return detail::packet::parse_bitfield<Start,End>::parse(i()); }
         void value(value_type v) { detail::packet::parse_bitfield<Start,End>::write(i(),v); }
diff --git a/Scheduler/Daemon.cc b/Scheduler/Daemon.cc
new file mode 100644 (file)
index 0000000..5252dcf
--- /dev/null
@@ -0,0 +1,339 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer NETwork research (NET)
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Daemon non-inline non-template implementation */
+
+#include "Daemon.hh"
+#include "Daemon.ih"
+
+// Custom includes
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include "../Utils/Exception.hh"
+#include "../Utils/membind.hh"
+
+//#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 "()")
+
+///////////////////////////////////////////////////////////////////////////
+// senf::Daemon
+
+prefix_ senf::Daemon::~Daemon()
+{}
+
+prefix_ void senf::Daemon::daemonize(bool v)
+{
+    daemonize_ = v;
+}
+
+prefix_ bool senf::Daemon::daemon()
+{
+    return daemonize_;
+}
+
+prefix_ void senf::Daemon::consoleLog(std::string path, StdStream which)
+{
+    int fd (-1);
+    if (! path.empty()) {
+        int fd (::open(path.c_str(), O_WRONLY | O_APPEND));
+        if (fd < 0)
+            throwErrno("::open()");
+    }
+    switch (which) {
+    case StdOut:
+        stdout_ = fd;
+        break;
+    case StdErr:
+        stderr_ = fd;
+        break;
+    case Both:
+        stdout_ = fd;
+        stderr_ = fd;
+        break;
+    }
+}
+
+prefix_ void senf::Daemon::pidFile(std::string f, bool unique)
+{
+    pidfile_ = f;
+    unique_ = unique;
+}
+
+prefix_ void senf::Daemon::detach()
+{
+    LIBC_CALL_RV( nul, ::open, ("/dev/null", O_WRONLY) );
+    LIBC_CALL( ::dup2, (nul, 1) );
+    LIBC_CALL( ::dup2, (nul, 2) );
+    LIBC_CALL( ::close, (nul) );
+}
+
+prefix_ int senf::Daemon::start(int argc, char const ** argv)
+{
+    argc_ = argc;
+    argv_ = argv;
+
+#   ifdef NDEBUG
+    try {
+#   endif
+
+    configure();
+    if (daemonize_)
+        fork();
+    if (! pidfile_.empty())
+        pidfileCreate();
+    main();
+
+#   ifdef NDEBUG
+    }
+    catch (std::exception & e) {
+        std::cerr << "\n*** Fatal exception: " << e.what() << std::endl;
+        return 1;
+    }
+    catch (...) {
+        std::cerr << "\n*** Fatal exception: (unknown)" << std::endl;
+        return 1;
+    }
+#   endif
+
+    return 0;
+}
+
+////////////////////////////////////////
+// protected members
+
+prefix_ senf::Daemon::Daemon()
+    : argc_(0), argv_(0), daemonize_(true), stdout_(-1), stderr_(-1), pidfile_(""), unique_(true),
+      detached_(false)
+{}
+
+////////////////////////////////////////
+// private members
+
+prefix_ void senf::Daemon::configure()
+{}
+
+prefix_ void senf::Daemon::main()
+{
+    init();
+    detach();
+    run();
+}
+
+prefix_ void senf::Daemon::init()
+{}
+
+prefix_ void senf::Daemon::run()
+{}
+
+prefix_ void senf::Daemon::fork()
+{
+    int coutpipe[2];
+    int cerrpipe[2];
+
+    LIBC_CALL_RV( nul, ::open, ("/dev/null", O_RDONLY) );
+    LIBC_CALL( ::dup2, (nul, 0) );
+    LIBC_CALL( ::close, (nul) );
+    LIBC_CALL( ::pipe, (coutpipe) );
+    LIBC_CALL( ::pipe, (cerrpipe) );
+
+    // We need to block the SIGCHLD signal here so we don't miss it, if the child
+    // dies immediately
+    ::sigset_t oldsig;
+    ::sigset_t cldsig;
+    ::sigemptyset(&cldsig);
+    LIBC_CALL( ::sigaddset, (&cldsig, SIGCHLD) );
+    LIBC_CALL( ::sigprocmask, (SIG_BLOCK, &cldsig, &oldsig) );
+    
+    LIBC_CALL_RV( pid, ::fork, () );
+
+    if (pid == 0) {
+        // Daemon process
+
+        LIBC_CALL( ::dup2, (coutpipe[1],1) );
+        LIBC_CALL( ::dup2, (cerrpipe[1],2) );
+        LIBC_CALL( ::close, (coutpipe[0]) );
+        LIBC_CALL( ::close, (coutpipe[1]) );
+        LIBC_CALL( ::close, (cerrpipe[0]) );
+        LIBC_CALL( ::close, (cerrpipe[1]) );
+        LIBC_CALL( ::setsid, () );
+        LIBC_CALL( ::sigprocmask, (SIG_SETMASK, &oldsig, 0) );
+        return;
+    }
+
+    LIBC_CALL( ::close, (coutpipe[1]) );
+    LIBC_CALL( ::close, (cerrpipe[1]) );
+
+    detail::DaemonWatcher watcher (pid, coutpipe[0], cerrpipe[0]);
+    watcher.run();
+
+    ::exit(0);
+
+}
+
+prefix_ void senf::Daemon::pidfileCreate()
+{}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::detail::DaemonWatcher
+
+prefix_ senf::detail::DaemonWatcher::DaemonWatcher(int pid, int coutpipe, int cerrpipe)
+    : childPid_(pid), coutpipe_(coutpipe), cerrpipe_(cerrpipe), 
+      coutForwarder_(coutpipe_, 1, senf::membind(&DaemonWatcher::pipeClosed, this)),
+      cerrForwarder_(cerrpipe_, 2, senf::membind(&DaemonWatcher::pipeClosed, this))
+{}
+
+prefix_ void senf::detail::DaemonWatcher::run()
+{
+    Scheduler::instance().registerSignal(SIGCHLD, senf::membind(&DaemonWatcher::childDied, this));
+    Scheduler::instance().process();
+}
+
+////////////////////////////////////////
+// private members
+
+prefix_ void senf::detail::DaemonWatcher::pipeClosed()
+{
+    if (! timerRunning_) {
+        Scheduler::instance().timeout(Scheduler::instance().eventTime() + ClockService::seconds(1),
+                                      senf::membind(&DaemonWatcher::childOk, this));
+        timerRunning_ = true;
+    }
+}
+
+prefix_ void senf::detail::DaemonWatcher::childDied()
+{
+    int status (0);
+    if (::waitpid(childPid_,&status,0) < 0) throwErrno("::waitpid()");
+    if (WIFSIGNALED(status)) {
+        ::signal(WTERMSIG(status),SIG_DFL);
+        ::kill(::getpid(), WTERMSIG(status));
+        // should not be reached
+        ::exit(1);
+    }
+    if (WEXITSTATUS(status) == 0)
+        ::exit(1);
+    ::exit(WEXITSTATUS(status));
+}
+
+prefix_ void senf::detail::DaemonWatcher::childOk()
+{
+    Scheduler::instance().terminate();
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::detail::DaemonWatcher::Forwarder
+
+prefix_ senf::detail::DaemonWatcher::Forwarder::Forwarder(int src, int dst, Callback cb)
+    : src_(src), dst_(dst), cb_(cb)
+{
+    Scheduler::instance().add(src_, senf::membind(&Forwarder::readData, this),
+                              Scheduler::EV_READ);
+}
+
+prefix_ senf::detail::DaemonWatcher::Forwarder::~Forwarder()
+{
+    if (src_ != -1)
+        Scheduler::instance().remove(src_);
+    if (dst_ != -1 && ! buffer_.empty())
+        Scheduler::instance().remove(dst_);
+}
+
+prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(Scheduler::EventId event)
+{
+    char buf[1024];
+    int n (0);
+    while (1) {
+        n = ::read(src_,buf,1024);
+        if (n<0) {
+            if (errno != EINTR) throwErrno("::read()");
+        } else 
+            break;
+    }
+    if (n == 0) {
+        // Hangup
+        Scheduler::instance().remove(src_);
+        if (buffer_.empty())
+            cb_(); 
+        src_ = -1;
+        return;
+    }
+    if (dst_ == -1)
+        // There was an error writing data -> drop it
+        return;
+    if (buffer_.empty())
+        Scheduler::instance().add(dst_, senf::membind(&Forwarder::writeData, this),
+                                  Scheduler::EV_WRITE);
+    buffer_.insert(buffer_.end(), buf, buf+n);
+}
+
+prefix_ void senf::detail::DaemonWatcher::Forwarder::writeData(Scheduler::EventId event)
+{    
+    if (event != Scheduler::EV_WRITE) {
+        // Broken pipe while writing data ? Not much, we can do here, we just drop the data
+        Scheduler::instance().remove(dst_);
+        dst_ = -1;
+        if (src_ == -1) cb_();
+        return;
+    }
+    char buf[1024];
+    int n (buffer_.size() > 1024 ? 1024 : buffer_.size());
+    std::copy(buffer_.begin(), buffer_.begin() + n, buf);
+    int w (::write(dst_, buf, n));
+    if (w < 0) {
+        if (errno != EINTR) throwErrno("::write()");
+        return;
+    }
+    buffer_.erase(buffer_.begin(), buffer_.begin()+w);
+    if (buffer_.empty()) {
+        Scheduler::instance().remove(dst_);
+        if (src_ == -1)
+            cb_();
+    }
+}
+
+#undef LIBC_CALL
+#undef LIBC_CALL_RV
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "Daemon.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Scheduler/Daemon.hh b/Scheduler/Daemon.hh
new file mode 100644 (file)
index 0000000..205dcad
--- /dev/null
@@ -0,0 +1,209 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer NETwork research (NET)
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Daemon public header */
+
+#ifndef HH_Daemon_
+#define HH_Daemon_ 1
+
+// Custom includes
+#include <boost/utility.hpp>
+
+//#include "Daemon.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+
+    /** \brief Daemon process
+
+        This class provides the infrastructure to implement robust daemon processes. A daemon
+        process is implemented by deriving from senf::Daemon and implementing the necessary
+        (virtual) member functions.
+        \code
+        class MyDaemon : public senf::Daemon
+        {
+            void configure() {
+                // Set configuration parameters like daemonize(), pidFile() etc.
+            }
+
+            void init() {
+                // Initialize application. Setup all necessary objects. After init()
+                // has completed, is startup should not fail anymore
+            }
+
+            void run() {
+                // Main application code should be called here.
+            }
+        };
+        \endcode
+
+        The startup procedure is divided into three steps:
+        \li First, configure() is called. configure() should be as simple as possible. It just needs
+            to set the daemon parameters. No further setup should be done here.
+        \li init() is called after fork() but while still connected to the terminal. init() should
+            do all necessary application setup. Here, all configuration or user errors should be
+            detected and properly diagnosed.
+        \li After init() returns, the application will detach from the terminal. Now run() is called
+            to enter the application main loop.
+
+        Since there are times, where separating init() and run() into two separate functions is
+        difficult, instead of defining init() and run(), the member main() may be defined. This
+        member must call detach() as soon as initialization is completed to detach from the
+        foreground terminal.
+      */
+    class Daemon : boost::noncopyable
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+        
+        /// Select standard stream to redirect
+        enum StdStream { 
+            StdOut  /** Standard output stream */
+        ,   StdErr  /** Standard error stream */
+        ,   Both    /** Both, standard output and error stream */
+        }; 
+
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///\{
+
+        virtual ~Daemon();
+
+        ///\}
+        ///\name Parameters
+        ///\{
+
+        void daemonize(bool);           ///< Configure whether to run in fore- or background
+        bool daemon();                  ///< \c true, if running as daemon
+
+        void consoleLog(std::string, StdStream which = Both); ///< Configure console log file
+                                        /**< May be called multiple times to set the log file
+                                             for stdout and stderr seperately. Any standard stream
+                                             not assigned to a log file will be redirected to
+                                             <tt>/dev/null</tt>. 
+
+                                             When running in the foreground, the log files will be
+                                             ignored. */
+
+        void pidFile(std::string, bool unique = true); ///< Configure pid file
+
+        ///\}
+        ///\name Auxiliary helpers
+        ///\{
+
+        void detach();                  ///< Detach into background now
+                                        /**< This is \e not the same as forking. The process will
+                                             already have forked into the background but until
+                                             detach() is called (either automatically after init()
+                                             returns or manually), the front end (foreground)
+                                             process will wait for the background process to ensure
+                                             successful startup. */
+
+        int argc();                     ///< Access command line parameter count
+        char const ** argv();           ///< Access command line parameters
+
+        void fail(int code=1);          ///< Terminate startup with failure
+
+        ///\}
+        
+        int start(int argc, char const ** argv); ///< Called from main() to launch daemon.
+                                        /**< Normally not called directly but from the
+                                             \ref SENF_DAEMON_MAIN macro. */
+
+    protected:
+        Daemon();
+
+#   ifdef DOXYGEN
+    protected:
+#   else
+    private:
+#   endif
+
+        virtual void configure();       ///< Called before forking to configure the daemon class
+        virtual void main();            ///< Called after forking to execute the main application
+                                        /**< The default implementation will call init(), detach()
+                                             and then run(). It is preferred to override init() and
+                                             run() if possible. */
+        virtual void init();            ///< Called to initialize the main application
+                                        /**< While init() is running, the application still is
+                                             connected to the controlling terminal. Error messages
+                                             will be shown to the user.
+
+                                             This member is only called, if the default main()
+                                             implementation is not overridden. */
+        virtual void run();             ///< Called to execute main application
+                                        /**< Called after detaching from the controlling
+                                             terminal.
+
+                                             This member is only called, if the default main()
+                                             implementation is not overridden. */
+    private:
+
+        void fork();
+        void pidfileCreate();
+
+        int argc_;
+        char const ** argv_;
+
+        bool daemonize_;
+        int stdout_;
+        int stderr_;
+        std::string pidfile_;
+        bool unique_;
+
+        bool detached_;
+    };
+
+    /** \brief Provide \c main() function
+
+        This macro will provide a \c main() function to launch the daemon process defined in \a
+        klass. \a klass must be a class derived from senf::Daemon.
+
+        \ingroup process
+     */
+#   define SENF_DAEMON_MAIN(klass)                                                                \
+        int main(int argc, char const ** argv)                                                    \
+        {                                                                                         \
+            klass instance;                                                                       \
+            return instance.start(argc, argv);                                                    \
+        }
+
+}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "Daemon.cci"
+//#include "Daemon.ct"
+//#include "Daemon.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Scheduler/Daemon.ih b/Scheduler/Daemon.ih
new file mode 100644 (file)
index 0000000..23af6ce
--- /dev/null
@@ -0,0 +1,100 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer NETwork research (NET)
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Daemon internal header */
+
+#ifndef IH_Daemon_
+#define IH_Daemon_ 1
+
+// Custom includes
+#include <deque>
+#include <boost/utility.hpp>
+#include <boost/function.hpp>
+#include "../Scheduler/Scheduler.hh"
+
+///////////////////////////////ih.p////////////////////////////////////////
+
+namespace senf {
+namespace detail {
+
+    /** \brief Internal: Watch daemon process for successful startup */
+    class DaemonWatcher
+        : boost::noncopyable
+    {
+    public:
+
+        DaemonWatcher(int pid, int coutpipe, int cerrpipe);
+
+        void run();
+
+    private:
+
+        class Forwarder 
+        {
+        public:
+            typedef boost::function<void ()> Callback;
+
+            Forwarder(int src, int dst, Callback cb);
+            ~Forwarder();
+
+        private:
+
+            void readData(Scheduler::EventId event);
+            void writeData(Scheduler::EventId event);
+
+            typedef std::deque<char> Buffer;
+            Buffer buffer_;
+            int src_;
+            int dst_;
+            Callback cb_;
+        };
+        
+        void pipeClosed();
+        void childDied();
+        void childOk();
+
+        int childPid_;
+        int coutpipe_;
+        int cerrpipe_;
+
+        Forwarder coutForwarder_;
+        Forwarder cerrForwarder_;
+
+        bool timerRunning_;
+    };
+
+}}
+
+///////////////////////////////ih.e////////////////////////////////////////
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Scheduler/Daemon.test.cc b/Scheduler/Daemon.test.cc
new file mode 100644 (file)
index 0000000..418ea9f
--- /dev/null
@@ -0,0 +1,101 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer NETwork research (NET)
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Daemon.test unit tests */
+
+//#include "Daemon.test.hh"
+//#include "Daemon.test.ih"
+
+// Custom includes
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <iostream>
+#include "Daemon.hh"
+#include "../Utils/Exception.hh"
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/test/test_tools.hpp>
+
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+namespace {
+
+    void delay(unsigned long milliseconds)
+    {
+        struct timespec ts;
+        ts.tv_sec = milliseconds / 1000;
+        ts.tv_nsec = (milliseconds % 1000) * 1000000;
+        while (nanosleep(&ts,&ts) < 0 && errno == EINTR) ;
+    }
+
+    class MyDaemon : public senf::Daemon
+    {
+        void configure() { std::cout << "Running configure()" << std::endl; }
+        void init() { std::cout << "Running init()" << std::endl; }
+        void run() {
+            delay(2000);
+            std::cout << "Running run()" << std::endl; 
+        }
+    };
+
+    int myMain(int argc, char const ** argv)
+    {
+        MyDaemon instance;
+        return instance.start(argc, argv);
+    }
+
+    int run(int argc, char const ** argv)
+    {
+        int pid (::fork());
+        if (pid < 0) senf::throwErrno("::fork()");
+        if (pid == 0) {
+            ::_exit(myMain(argc, argv));
+        }
+        int status;
+        if (::waitpid(pid, &status, 0) < 0) senf::throwErrno("::waitpid()");
+         return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+    }
+
+}
+
+BOOST_AUTO_UNIT_TEST(testDaemon)
+{
+    char const * args[] = { "run", 0 };
+    BOOST_CHECK_EQUAL( run(1,args), 0 );
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
index 09628f1..9f222bf 100644 (file)
@@ -170,7 +170,7 @@ prefix_ void senf::Scheduler::sigHandler(int signal, ::siginfo_t * siginfo, void
     // queue. Since signals are only unblocked during epoll_wait, we even wouldn't need to
     // synchronize access to that queue any further.
 
-    ::write(instance().sigpipe_[1], siginfo, sizeof(siginfo));
+    ::write(instance().sigpipe_[1], siginfo, sizeof(*siginfo));
 
     // We ignore errors. The file handle is set to non-blocking IO. If any failure occurs (pipe
     // full), the signal will be dropped. That's like kernel signal handling which may also drop
@@ -250,10 +250,12 @@ prefix_ void senf::Scheduler::process()
         // Check the signal queue
         if (ev.data.fd == sigpipe_[0]) {
             ::siginfo_t siginfo;
-            if (::read(sigpipe_[0], &siginfo, sizeof(siginfo)) < int(sizeof(siginfo)))
+            if (::read(sigpipe_[0], &siginfo, sizeof(siginfo)) < int(sizeof(siginfo))) {
                 // We ignore truncated records which may only occur if the signal
                 // queue became filled up
+                SENF_LOG((senf::log::IMPORTANT)("Truncated signal record!"));
                 continue;
+            }
             if (siginfo.si_signo < int(sigHandlers_.size()) && sigHandlers_[siginfo.si_signo])
                 sigHandlers_[siginfo.si_signo]();
             continue;
index 7696a64..1ad6896 100644 (file)
@@ -37,7 +37,7 @@
 #include <boost/call_traits.hpp>
 #include <boost/integer.hpp>
 #include "ClockService.hh"
-#include "../Utils/Logger/Target.hh"
+#include "../Utils/Logger.hh"
 
 //#include "scheduler.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
@@ -158,6 +158,9 @@ namespace senf {
         : boost::noncopyable
     {
     public:
+
+        SENF_LOG_CLASS_AREA();
+
         ///////////////////////////////////////////////////////////////////////////
         // Types
 
index a9bd18e..c26b640 100644 (file)
@@ -267,15 +267,19 @@ BOOST_AUTO_UNIT_TEST(scheduler)
     BOOST_REQUIRE_EQUAL( size, 2 );
     buffer[size]=0;
     BOOST_CHECK_EQUAL( buffer, "OK" );
+    BOOST_CHECK_NO_THROW( Scheduler::instance().remove(handle) );
 
-    BOOST_CHECK_NO_THROW( Scheduler::instance().timeout(
-                              ClockService::now()+ClockService::milliseconds(200),&timeout) );
+    unsigned tid (Scheduler::instance().timeout(
+                      ClockService::now()+ClockService::milliseconds(200),&timeout));
     BOOST_CHECK_NO_THROW( Scheduler::instance().registerSignal(SIGUSR1, &sigusr) );
     t = ClockService::now();
     ::kill(::getpid(), SIGUSR1);
     delay(100);
     BOOST_CHECK_NO_THROW( Scheduler::instance().process() ); 
     BOOST_CHECK_PREDICATE( is_close, (ClockService::now()) (t+ClockService::milliseconds(100)) );
+    BOOST_CHECK_PREDICATE( is_close, (sigtime) (t+ClockService::milliseconds(100)) );
+    Scheduler::instance().cancelTimeout(tid);
+    BOOST_CHECK_NO_THROW( Scheduler::instance().unregisterSignal(SIGUSR1) );
 
     ///////////////////////////////////////////////////////////////////////////
 
index 1896a5c..1d33a6e 100644 (file)
 //#include "DaemonTools.ih"
 
 // Custom includes
-#include <sys/types.h>
+#include <errno.h>
 #include <sys/stat.h>
-#include <unistd.h>
+#include <sys/types.h>
 #include <fcntl.h>
-#include <errno.h>
 #include "Exception.hh"
 
 //#include "DaemonTools.mpp"
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
-///////////////////////////////////////////////////////////////////////////
-// senf::Daemon
-
-prefix_ senf::Daemon::~Daemon()
-{}
-
-prefix_ void senf::Daemon::daemonize(bool v)
-{
-    daemonize_ = v;
-}
-
-prefix_ bool senf::Daemon::daemon()
-{
-    return daemonize_;
-}
-
-prefix_ void senf::Daemon::consoleLog(std::string path, StdStream which)
-{
-    int fd (-1);
-    if (! path.empty()) {
-        int fd (::open(path.c_str(), O_WRONLY | O_APPEND));
-        if (fd < 0)
-            throwErrno("open()");
-    }
-    switch (which) {
-    case StdOut:
-        stdout_ = fd;
-        break;
-    case StdErr:
-        stderr_ = fd;
-        break;
-    case Both:
-        stdout_ = fd;
-        stderr_ = fd;
-        break;
-    }
-}
-
-prefix_ void senf::Daemon::pidFile(std::string f, bool unique)
-{
-    pidfile_ = f;
-    unique_ = unique;
-}
-
-prefix_ void senf::Daemon::detach()
-{}
-
-prefix_ int senf::Daemon::start(int argc, char const ** argv)
-{
-    argc_ = argc;
-    argv_ = argv;
-
-#   ifdef NDEBUG
-    try {
-#   endif
-
-    configure();
-    if (daemonize_)
-        fork();
-    if (! pidfile_.empty())
-        pidfileCreate();
-    main();
-
-#   ifdef NDEBUG
-    }
-    catch (std::exception & e) {
-        std::cerr << "\n*** Fatal exception: " << e.what() << std::endl;
-        return 1;
-    }
-    catch (...) {
-        std::cerr << "\n*** Fatal exception: (unknown)" << std::endl;
-        return 1;
-    }
-#   endif
-
-    return 0;
-}
-
-////////////////////////////////////////
-// protected members
-
-prefix_ senf::Daemon::Daemon()
-    : argc_(0), argv_(0), daemonize_(true), stdout_(-1), stderr_(-1), pidfile_(""), unique_(true),
-      detached_(false)
-{}
-
-////////////////////////////////////////
-// private members
-
-prefix_ void senf::Daemon::configure()
-{}
-
-prefix_ void senf::Daemon::main()
-{
-    init();
-    detach();
-    run();
-}
-
-prefix_ void senf::Daemon::init()
-{}
-
-prefix_ void senf::Daemon::run()
-{}
-
-prefix_ void senf::Daemon::fork()
-{
-    
-}
-
-prefix_ void senf::Daemon::pidfileCreate()
-{}
-
-///////////////////////////////////////////////////////////////////////////
-
 prefix_ void senf::daemonize()
 {
     int pid = fork();
index 46d510a..466e592 100644 (file)
 
 // Custom includes
 #include <string>
-#include <boost/utility.hpp>
 
 //#include "DaemonTools.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
 
 namespace senf {
 
-    /** \brief Daemon process
-
-        This class provides the infrastructure to implement robust daemon processes. A daemon
-        process is implemented by deriving from senf::Daemon and implementing the necessary
-        (virtual) member functions.
-        \code
-        class MyDaemon : public senf::Daemon
-        {
-            void configure() {
-                // Set configuration parameters like daemonize(), pidFile() etc.
-            }
-
-            void init() {
-                // Initialize application. Setup all necessary objects. After init()
-                // has completed, is startup should not fail anymore
-            }
-
-            void run() {
-                // Main application code should be called here.
-            }
-        };
-        \endcode
-
-        The startup procedure is divided into three steps:
-        \li First, configure() is called. configure() should be as simple as possible. It just needs
-            to set the daemon parameters. No further setup should be done here.
-        \li init() is called after fork() but while still connected to the terminal. init() should
-            do all necessary application setup. Here, all configuration or user errors should be
-            detected and properly diagnosed.
-        \li After init() returns, the application will detach from the terminal. Now run() is called
-            to enter the application main loop.
-
-        Since there are times, where separating init() and run() into two separate functions is
-        difficult, instead of defining init() and run(), the member main() may be defined. This
-        member must call detach() as soon as initialization is completed to detach from the
-        foreground terminal.
-
-        
-        
-        \ingroup process
-      */
-    class Daemon : boost::noncopyable
-    {
-    public:
-        ///////////////////////////////////////////////////////////////////////////
-        // Types
-        
-        /// Select standard stream to redirect
-        enum StdStream { 
-            StdOut /** Standard output stream */
-        ,   StdErr /** Standard error stream */
-        ,    Both  /** Both, standard output and error stream */
-        }; 
-
-        ///////////////////////////////////////////////////////////////////////////
-        ///\name Structors and default members
-        ///\{
-
-        virtual ~Daemon();
-
-        ///\}
-        ///\name Parameters
-        ///\{
-
-        void daemonize(bool);           ///< Configure whether to run in fore- or background
-        bool daemon();                  ///< \c true, if running as daemon
-
-        void consoleLog(std::string, StdStream which = Both); ///< Configure console log file
-                                        /**< May be called multiple times to set the log file
-                                             for stdout and stderr seperately. Any standard stream
-                                             not assigned to a log file will be redirected to
-                                             <tt>/dev/null</tt>. 
-
-                                             When running in the foreground, the log files will be
-                                             ignored. */
-
-        void pidFile(std::string, bool unique = true); ///< Configure pid file
-
-        ///\}
-        ///\name Auxiliary helpers
-        ///\{
-
-        void detach();                  ///< Detach into background now
-                                        /**< This is \e not the same as forking. The process will
-                                             already have forked into the background but until
-                                             detach() is called (either automatically after init()
-                                             returns or manually), the front end (foreground)
-                                             process will wait for the background process to ensure
-                                             successful startup. */
-
-        int argc();                     ///< Access command line parameter count
-        char const ** argv();           ///< Access command line parameters
-
-        void fail(int code=1);          ///< Terminate startup with failure
-
-        ///\}
-        
-        int start(int argc, char const ** argv); ///< Called from main() to launch daemon.
-                                        /**< Normally not called directly but from the
-                                             \ref SENF_DAEMON_MAIN macro. */
-
-    protected:
-        Daemon();
-
-#   ifdef DOXYGEN
-    protected:
-#   else
-    private:
-#   endif
-
-        virtual void configure();       ///< Called before forking to configure the daemon class
-        virtual void main();            ///< Called after forking to execute the main application
-                                        /**< The default implementation will call init(), detach()
-                                             and then run(). It is preferred to override init() and
-                                             run() if possible. */
-        virtual void init();            ///< Called to initialize the main application
-                                        /**< While init() is running, the application still is
-                                             connected to the controlling terminal. Error messages
-                                             will be shown to the user.
-
-                                             This member is only called, if the default main()
-                                             implementation is not overridden. */
-        virtual void run();             ///< Called to execute main application
-                                        /**< Called after detaching from the controlling
-                                             terminal.
-
-                                             This member is only called, if the default main()
-                                             implementation is not overridden. */
-    private:
-
-        void fork();
-        void pidfileCreate();
-
-        int argc_;
-        char const ** argv_;
-
-        bool daemonize_;
-        int stdout_;
-        int stderr_;
-        std::string pidfile_;
-        bool unique_;
-
-        bool detached_;
-    };
-
-    /** \brief Provide \c main() function
-
-        This macro will provide a \c main() function to launch the daemon process defined in \a
-        klass. \a klass must be a class derived from senf::Daemon.
-
-        \ingroup process
-     */
-#   define SENF_DAEMON_MAIN(klass)                                                                \
-        int main(int argc, char const ** argv)                                                    \
-        {                                                                                         \
-            klass instance;                                                                       \
-            return instance.start(argc, argv);                                                    \
-        }
-
     /// \addtogroup process
     /// @{
 
index 5370f97..213cf03 100644 (file)
@@ -38,6 +38,7 @@
 #include <boost/type_traits/is_convertible.hpp>
 #include "../mpl.hh"
 #include "Config.hh"
+#include "Target.hh"
 
 ///////////////////////////////ih.p////////////////////////////////////////
 
@@ -117,7 +118,8 @@ namespace detail {
 
         static bool enabled() { 
             return compileEnabled
-                && Base::area::instance().limit(Base::stream::instance()) <= level::value;
+                && ( senf::log::detail::TargetRegistry::instance().fallbackRouting() ||
+                     Base::area::instance().limit(Base::stream::instance()) <= level::value );
         }
     };
 
index 5c77795..ed2c0d9 100644 (file)
@@ -104,6 +104,11 @@ prefix_ void senf::log::detail::TargetRegistry::routed()
     fallbackRouting_ = false;
 }
 
+prefix_ bool senf::log::detail::TargetRegistry::fallbackRouting()
+{
+    return fallbackRouting_;
+}
+
 ////////////////////////////////////////
 // private members
 
index f6cdc85..668bacc 100644 (file)
@@ -49,6 +49,7 @@ namespace detail {
         void timeSource(std::auto_ptr<TimeSource> source);
 
         void routed();
+        bool fallbackRouting();
 
     private:
         TargetRegistry();
index 799d529..c9bcdeb 100644 (file)
 
 // Custom includes
 
+#ifdef SENF_LOG_CONF
+#undef SENF_LOG_CONF
+#endif
+
 #define SENF_LOG_CONF (( (senf)(log)(Debug), (_), NOTICE )) \
                       (( (senf)(log)(test)(myStream), (senf)(log)(test)(Foo), VERBOSE ))