working signal handler
authorGuilherme Maciel Ferreira <guilherme.maciel.ferreira@intra2net.com>
Tue, 3 May 2011 08:10:15 +0000 (10:10 +0200)
committerGuilherme Maciel Ferreira <guilherme.maciel.ferreira@intra2net.com>
Tue, 3 May 2011 08:10:59 +0000 (10:10 +0200)
lib/boost-signal_handler/LICENSE_1_0.txt [new file with mode: 0644]
lib/boost-signal_handler/boost/asio/posix/basic_signal_handler.hpp [new file with mode: 0644]
lib/boost-signal_handler/boost/asio/posix/basic_signal_handler_service.hpp [new file with mode: 0644]
lib/boost-signal_handler/boost/asio/posix/signal_handler.hpp [new file with mode: 0644]
lib/boost-signal_handler/boost/asio/posix/signal_handler_impl.hpp [new file with mode: 0644]
src/CMakeLists.txt
src/host/pingscheduler.cpp
src/host/pingscheduler.h
src/main.cpp

diff --git a/lib/boost-signal_handler/LICENSE_1_0.txt b/lib/boost-signal_handler/LICENSE_1_0.txt
new file mode 100644 (file)
index 0000000..1dad8e9
--- /dev/null
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003\r
+\r
+Permission is hereby granted, free of charge, to any person or organization\r
+obtaining a copy of the software and accompanying documentation covered by\r
+this license (the "Software") to use, reproduce, display, distribute,\r
+execute, and transmit the Software, and to prepare derivative works of the\r
+Software, and to permit third-parties to whom the Software is furnished to\r
+do so, all subject to the following:\r
+\r
+The copyright notices in the Software and this entire statement, including\r
+the above license grant, this restriction and the following disclaimer,\r
+must be included in all copies of the Software, in whole or in part, and\r
+all derivative works of the Software, unless such copies or derivative\r
+works are solely in the form of machine-executable object code generated by\r
+a source language processor.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\r
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\r
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\r
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
+DEALINGS IN THE SOFTWARE.\r
diff --git a/lib/boost-signal_handler/boost/asio/posix/basic_signal_handler.hpp b/lib/boost-signal_handler/boost/asio/posix/basic_signal_handler.hpp
new file mode 100644 (file)
index 0000000..12602ce
--- /dev/null
@@ -0,0 +1,62 @@
+// \r
+// Copyright (c) 2009, 2010 Boris Schaeling <boris@highscore.de> \r
+// \r
+// Distributed under the Boost Software License, Version 1.0. (See accompanying \r
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) \r
+// \r
+\r
+#ifndef BOOST_ASIO_POSIX_BASIC_SIGNAL_HANDLER_HPP \r
+#define BOOST_ASIO_POSIX_BASIC_SIGNAL_HANDLER_HPP \r
+\r
+#include <boost/asio.hpp> \r
+#include <signal.h> \r
+\r
+namespace boost { \r
+namespace asio { \r
+namespace posix { \r
+\r
+template <typename Service> \r
+class basic_signal_handler \r
+    : public boost::asio::basic_io_object<Service> \r
+{ \r
+public: \r
+    explicit basic_signal_handler(boost::asio::io_service &io_service) \r
+        : boost::asio::basic_io_object<Service>(io_service) \r
+    { \r
+    } \r
+\r
+    void add_signal(int signal, int flags = 0, sigset_t *mask = 0) \r
+    { \r
+        this->service.add_signal(this->implementation, signal, flags, mask); \r
+    } \r
+\r
+    void remove_signal(int signal) \r
+    { \r
+        this->service.remove_signal(this->implementation, signal); \r
+    } \r
+\r
+    int wait() \r
+    { \r
+        boost::system::error_code ec; \r
+        int signal = this->service.wait(this->implementation, ec); \r
+        boost::asio::detail::throw_error(ec); \r
+        return signal; \r
+    } \r
+\r
+    int wait(boost::system::error_code &ec) \r
+    { \r
+        return this->service.wait(this->implementation, ec); \r
+    } \r
+\r
+    template <typename Handler> \r
+    void async_wait(Handler handler) \r
+    { \r
+        this->service.async_wait(this->implementation, handler); \r
+    } \r
+}; \r
+\r
+} \r
+} \r
+} \r
+\r
+#endif \r
diff --git a/lib/boost-signal_handler/boost/asio/posix/basic_signal_handler_service.hpp b/lib/boost-signal_handler/boost/asio/posix/basic_signal_handler_service.hpp
new file mode 100644 (file)
index 0000000..27c9cd7
--- /dev/null
@@ -0,0 +1,193 @@
+// \r
+// Copyright (c) 2009, 2010 Boris Schaeling <boris@highscore.de> \r
+// \r
+// Distributed under the Boost Software License, Version 1.0. (See accompanying \r
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) \r
+// \r
+\r
+#ifndef BOOST_ASIO_POSIX_BASIC_SIGNAL_HANDLER_SERVICE_HPP \r
+#define BOOST_ASIO_POSIX_BASIC_SIGNAL_HANDLER_SERVICE_HPP \r
+\r
+#include "signal_handler_impl.hpp" \r
+#include <boost/asio.hpp> \r
+#include <boost/thread.hpp> \r
+#include <boost/bind.hpp> \r
+#include <boost/shared_ptr.hpp> \r
+#include <boost/weak_ptr.hpp> \r
+#include <boost/scoped_ptr.hpp> \r
+#include <boost/system/error_code.hpp> \r
+#include <boost/system/system_error.hpp> \r
+#include <cstring> \r
+#include <signal.h> \r
+#include <unistd.h> \r
+\r
+namespace boost { \r
+namespace asio { \r
+namespace posix { \r
+\r
+template <typename SignalHandlerImplementation = signal_handler_impl> \r
+class basic_signal_handler_service \r
+    : public boost::asio::io_service::service \r
+{ \r
+public: \r
+    static boost::asio::io_service::id id; \r
+\r
+    explicit basic_signal_handler_service(boost::asio::io_service &io_service) \r
+        : boost::asio::io_service::service(io_service), \r
+        async_wait_work_(new boost::asio::io_service::work(async_wait_io_service_)), \r
+        async_wait_thread_(boost::bind(&boost::asio::io_service::run, &async_wait_io_service_)) \r
+    { \r
+    } \r
+\r
+    virtual ~basic_signal_handler_service()\r
+    { \r
+        // The async_wait thread will finish when async_wait_work_ is reset as all asynchronous \r
+        // operations have been aborted and were discarded before (in destroy). \r
+        async_wait_work_.reset(); \r
+\r
+        // Event processing is stopped to discard queued operations. \r
+        async_wait_io_service_.stop(); \r
+\r
+        // The async_wait thread is joined to make sure the signal handler service is \r
+        // destroyed _after_ the thread is finished (not that the thread tries to access \r
+        // instance properties which don't exist anymore). \r
+        async_wait_thread_.join(); \r
+    } \r
+\r
+    typedef boost::shared_ptr<SignalHandlerImplementation> implementation_type; \r
+\r
+    void construct(implementation_type &impl) \r
+    { \r
+        // We must count the I/O objects and can't rely on the shared pointer impl_ \r
+        // as an asynchronous call can hold a shared pointer at any time, too, thus \r
+        // preventing the implementation to be destroyed in destroy(). \r
+        ++count_; \r
+\r
+        implementation_type shared_impl = impl_.lock(); \r
+        if (shared_impl) \r
+        { \r
+            impl = shared_impl; \r
+        } \r
+        else \r
+        { \r
+            impl.reset(new SignalHandlerImplementation()); \r
+            impl_ = impl; \r
+            fd_ = impl->write_end(); \r
+        } \r
+    } \r
+\r
+    void destroy(implementation_type &impl) \r
+    { \r
+        // If an asynchronous call is currently waiting for a signal \r
+        // we must interrupt the blocked call to make sure it returns. \r
+        if (!--count_) \r
+            impl->destroy(); \r
+\r
+        impl.reset(); \r
+    } \r
+\r
+    void add_signal(implementation_type &impl, int signal, int flags, sigset_t *mask) \r
+    { \r
+        struct sigaction act, oldact; \r
+\r
+        std::memset(&act, 0, sizeof(struct sigaction)); \r
+        act.sa_handler = basic_signal_handler_service<SignalHandlerImplementation>::signal_handler; \r
+        act.sa_flags = flags; \r
+        if (mask) \r
+            act.sa_mask = *mask; \r
+\r
+        int res = sigaction(signal, &act, &oldact); \r
+        if (res == -1) \r
+        { \r
+            boost::system::system_error e(boost::system::error_code(errno, boost::system::get_system_category()), "boost::asio::posix::basic_signal_handler_service::add_signal: sigaction failed"); \r
+            boost::throw_exception(e); \r
+        } \r
+\r
+        impl->add_signal(signal, oldact); \r
+    } \r
+\r
+    void remove_signal(implementation_type &impl, int signal) \r
+    { \r
+        impl->remove_signal(signal); \r
+    } \r
+\r
+    int wait(implementation_type &impl, boost::system::error_code &ec) \r
+    { \r
+        return impl->wait(ec); \r
+    } \r
+\r
+    template <typename Handler> \r
+    class wait_operation \r
+    { \r
+    public: \r
+        wait_operation(implementation_type &impl, boost::asio::io_service &io_service, Handler handler) \r
+            : impl_(impl), \r
+            io_service_(io_service), \r
+            work_(io_service), \r
+            handler_(handler) \r
+        { \r
+        } \r
+\r
+        void operator()() const \r
+        { \r
+            implementation_type impl = impl_.lock(); \r
+            if (impl) \r
+            { \r
+                boost::system::error_code ec; \r
+                int signal = impl->wait(ec); \r
+                this->io_service_.post(boost::asio::detail::bind_handler(handler_, ec, signal)); \r
+            } \r
+            else \r
+            { \r
+                this->io_service_.post(boost::asio::detail::bind_handler(handler_, boost::asio::error::operation_aborted, 0)); \r
+            } \r
+        } \r
+\r
+    private: \r
+        boost::weak_ptr<SignalHandlerImplementation> impl_; \r
+        boost::asio::io_service &io_service_; \r
+        boost::asio::io_service::work work_; \r
+        Handler handler_; \r
+    }; \r
+\r
+    template <typename Handler> \r
+    void async_wait(implementation_type &impl, Handler handler) \r
+    { \r
+        this->async_wait_io_service_.post(wait_operation<Handler>(impl, this->get_io_service(), handler)); \r
+    } \r
+\r
+private: \r
+    void shutdown_service() \r
+    { \r
+    } \r
+\r
+    static void signal_handler(int signal) \r
+    { \r
+        while (::write(fd_, &signal, sizeof(signal)) == -1 && errno == EINTR); \r
+    } \r
+\r
+    static int count_; \r
+    static boost::weak_ptr<SignalHandlerImplementation> impl_; \r
+    static int fd_; \r
+    boost::asio::io_service async_wait_io_service_; \r
+    boost::scoped_ptr<boost::asio::io_service::work> async_wait_work_; \r
+    boost::thread async_wait_thread_; \r
+}; \r
+\r
+template <typename SignalHandlerImplementation> \r
+boost::asio::io_service::id basic_signal_handler_service<SignalHandlerImplementation>::id; \r
+\r
+template <typename SignalHandlerImplementation> \r
+int basic_signal_handler_service<SignalHandlerImplementation>::count_ = 0; \r
+\r
+template <typename SignalHandlerImplementation> \r
+boost::weak_ptr<SignalHandlerImplementation> basic_signal_handler_service<SignalHandlerImplementation>::impl_; \r
+\r
+template <typename SignalHandlerImplementation> \r
+int basic_signal_handler_service<SignalHandlerImplementation>::fd_; \r
+\r
+} \r
+} \r
+} \r
+\r
+#endif \r
diff --git a/lib/boost-signal_handler/boost/asio/posix/signal_handler.hpp b/lib/boost-signal_handler/boost/asio/posix/signal_handler.hpp
new file mode 100644 (file)
index 0000000..ea6cd1f
--- /dev/null
@@ -0,0 +1,24 @@
+// \r
+// Copyright (c) 2009, 2010 Boris Schaeling <boris@highscore.de> \r
+// \r
+// Distributed under the Boost Software License, Version 1.0. (See accompanying \r
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) \r
+// \r
+\r
+#ifndef BOOST_ASIO_POSIX_SIGNAL_HANDLER_HPP \r
+#define BOOST_ASIO_POSIX_SIGNAL_HANDLER_HPP \r
+\r
+#include "basic_signal_handler.hpp" \r
+#include "basic_signal_handler_service.hpp" \r
+\r
+namespace boost { \r
+namespace asio { \r
+namespace posix { \r
+\r
+typedef basic_signal_handler<basic_signal_handler_service<> > signal_handler; \r
+\r
+} \r
+} \r
+} \r
+\r
+#endif \r
diff --git a/lib/boost-signal_handler/boost/asio/posix/signal_handler_impl.hpp b/lib/boost-signal_handler/boost/asio/posix/signal_handler_impl.hpp
new file mode 100644 (file)
index 0000000..084a04b
--- /dev/null
@@ -0,0 +1,135 @@
+// \r
+// Copyright (c) 2009, 2010 Boris Schaeling <boris@highscore.de> \r
+// \r
+// Distributed under the Boost Software License, Version 1.0. (See accompanying \r
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) \r
+// \r
+\r
+#ifndef BOOST_ASIO_POSIX_SIGNAL_HANDLER_IMPL_HPP \r
+#define BOOST_ASIO_POSIX_SIGNAL_HANDLER_IMPL_HPP \r
+\r
+#include <boost/asio.hpp> \r
+#include <boost/thread.hpp> \r
+#include <boost/bind.hpp> \r
+#include <boost/system/error_code.hpp> \r
+#include <boost/system/system_error.hpp> \r
+#include <map> \r
+#include <deque> \r
+#include <signal.h> \r
+#include <unistd.h> \r
+\r
+namespace boost { \r
+namespace asio { \r
+namespace posix { \r
+\r
+class signal_handler_impl \r
+{ \r
+public: \r
+    signal_handler_impl() \r
+        : sd_(signal_handler_io_service_), \r
+        run_(true) \r
+    { \r
+        int res = pipe(fds_); \r
+        if (res == -1) \r
+        { \r
+            boost::system::system_error e(boost::system::error_code(errno, boost::system::get_system_category()), "boost::asio::posix::signal_handler_impl::signal_handler_impl: pipe failed"); \r
+            boost::throw_exception(e); \r
+        } \r
+\r
+        sd_.assign(fds_[0]); \r
+        begin_read(); \r
+        signal_handler_thread_ = boost::thread(boost::bind(&boost::asio::io_service::run, &signal_handler_io_service_)); \r
+    } \r
+\r
+    ~signal_handler_impl() \r
+    { \r
+        for (sigactions_t::iterator it = sigactions_.begin(); it != sigactions_.end(); ++it) \r
+            sigaction(it->first, &it->second, 0); \r
+\r
+        close(fds_[1]); \r
+        sd_.close(); \r
+\r
+        signal_handler_io_service_.stop(); \r
+        signal_handler_thread_.join(); \r
+    } \r
+\r
+    int write_end() const \r
+    { \r
+      return fds_[1]; \r
+    } \r
+\r
+    void add_signal(int signal, struct sigaction oldact) \r
+    { \r
+        sigactions_.insert(sigactions_t::value_type(signal, oldact)); \r
+    } \r
+\r
+    void remove_signal(int signal) \r
+    { \r
+        sigactions_t::iterator it = sigactions_.find(signal); \r
+        if (it != sigactions_.end()) \r
+        { \r
+            sigaction(signal, &it->second, 0); \r
+            sigactions_.erase(it); \r
+        } \r
+    } \r
+\r
+    void destroy() \r
+    { \r
+        boost::unique_lock<boost::mutex> lock(pending_signals_mutex_); \r
+        run_ = false; \r
+        pending_signals_cond_.notify_all(); \r
+    } \r
+\r
+    int wait(boost::system::error_code &ec) \r
+    { \r
+        boost::unique_lock<boost::mutex> lock(pending_signals_mutex_); \r
+        while (run_ && pending_signals_.empty()) \r
+            pending_signals_cond_.wait(lock); \r
+        int signal = 0; \r
+        if (!pending_signals_.empty()) \r
+        { \r
+            signal = pending_signals_.front(); \r
+            pending_signals_.pop_front(); \r
+        } \r
+        else \r
+            ec = boost::asio::error::operation_aborted; \r
+        return signal; \r
+    } \r
+\r
+private: \r
+    void begin_read() \r
+    { \r
+      boost::asio::async_read(sd_, boost::asio::buffer(signal_buffer_), \r
+          boost::bind(&signal_handler_impl::end_read, this, \r
+          boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); \r
+    } \r
+\r
+    void end_read(const boost::system::error_code &ec, std::size_t bytes_transferred) \r
+    { \r
+      if (!ec) \r
+      { \r
+        boost::unique_lock<boost::mutex> lock(pending_signals_mutex_); \r
+        pending_signals_.push_back(signal_buffer_[0]); \r
+        pending_signals_cond_.notify_all(); \r
+        begin_read(); \r
+      } \r
+    } \r
+\r
+    int fds_[2]; \r
+    boost::asio::io_service signal_handler_io_service_; \r
+    boost::thread signal_handler_thread_; \r
+    boost::asio::posix::stream_descriptor sd_; \r
+    int signal_buffer_[1]; \r
+    typedef std::map<int, struct sigaction> sigactions_t; \r
+    sigactions_t sigactions_; \r
+    boost::mutex pending_signals_mutex_; \r
+    boost::condition_variable_any pending_signals_cond_; \r
+    bool run_; \r
+    std::deque<int> pending_signals_; \r
+}; \r
+\r
+} \r
+} \r
+} \r
+\r
+#endif \r
index d893a3f..a3ff1f6 100644 (file)
@@ -12,6 +12,9 @@ link_directories(${Boost_LIBRARY_DIRS})
 # package: boost-net-dns
 include_directories(${CMAKE_SOURCE_DIR}/lib/boost-net-dns)
 
+# package: boost-signal_handler
+include_directories(${CMAKE_SOURCE_DIR}/lib/boost-signal_handler)
+
 # package: libi2ncommon
 pkg_check_modules(I2NCOMMON REQUIRED libi2ncommon)
 include_directories(${I2NCOMMON_INCLUDE_DIRS})
index b04f534..f5252ee 100644 (file)
@@ -69,6 +69,15 @@ void PingScheduler::wait_pinging_thread()
     Thread.join();
 }
 
+/**
+ * @brief Stops pinging the host. Request the thread to finish, which makes the
+ * any blocking wait (join) on this object to return.
+ */
+void PingScheduler::stop_pinging_thread()
+{
+    stop_pinging();
+}
+
 bool PingScheduler::start_pinging()
 {
     bool address_resolved = resolve_ping_address();
index be41dab..5337b40 100644 (file)
@@ -37,14 +37,17 @@ public:
 
     bool start_pinging_thread();
     void wait_pinging_thread();
+    void stop_pinging_thread();
+
+private:
     bool start_pinging();
     void stop_pinging();
 
-private:
     bool resolve_ping_address();
     bool ping( const std::string &destination_ip ) const;
     void setup_next_ping();
     void schedule_next_ping();
+
     void update_ping_statistics( const bool ping_success );
     void update_ping_interval();
     void update_ping_elapsed_time();
index 46f7c09..263678d 100644 (file)
@@ -1,7 +1,10 @@
+#include <signal.h>
+
 #include <vector>
 #include <iostream>
 
 #include <boost/asio.hpp>
+#include <boost/asio/posix/signal_handler.hpp>
 #include <boost/foreach.hpp>
 #include <boost/shared_ptr.hpp>
 
@@ -54,13 +57,18 @@ LinkStatusAnalyzerItem get_status_notifier(
     return link_analyzer;
 }
 
-void init_log()
+void init_logger()
 {
     I2n::Logger::enable_syslog( I2n::Logger::Facility::User );
     // TODO: Change back to Notice log level
     I2n::Logger::set_log_level( I2n::Logger::LogLevel::Info );
 }
 
+void init_signal_handler()
+{
+    // TODO
+}
+
 void init_pingers(
         const ConfigurationItem &configuration,
         const LinkStatusAnalyzerItem &status_notifier,
@@ -94,7 +102,7 @@ void start_pingers(
         const PingSchedulerList &scheduler_list
 )
 {
-    // start each scheduler
+    // start each ping scheduler
     BOOST_FOREACH( PingSchedulerItem scheduler, scheduler_list )
     {
         bool started = scheduler->start_pinging_thread();
@@ -105,16 +113,39 @@ void start_pingers(
         }
     }
 
+#if 0
     // Main loop to handle scheduled ping requests
     BOOST_FOREACH( PingSchedulerItem scheduler, scheduler_list )
     {
         scheduler->wait_pinging_thread();
     }
+#endif
+}
+
+void stop_pingers(
+        const PingSchedulerList &scheduler_list
+)
+{
+    // stop each ping scheduler
+    BOOST_FOREACH( PingSchedulerItem scheduler, scheduler_list )
+    {
+        scheduler->stop_pinging_thread();
+    }
+}
+
+void handle_SIGINT(
+        const boost::system::error_code &ec,
+        int signal
+)
+{
+//    cerr << "handle_SIGINT" << endl;
+    // TODO may I call stop_pingers() them from here?
+//    stop_pingers();
 }
 
 int main( int argc, char *argv[] )
 {
-    init_log();
+    init_logger();
 
     ConfigurationItem configuration = get_configuration( argc, argv );
     if ( configuration.get() != NULL )
@@ -131,6 +162,13 @@ int main( int argc, char *argv[] )
         init_pingers( configuration, status_notifier, &scheduler_list );
 
         start_pingers( scheduler_list );
+
+        // TODO signal stuff to method
+        io_service io_serv;
+        boost::asio::posix::signal_handler sh( io_serv );
+        sh.add_signal( SIGINT );
+        sh.async_wait( handle_SIGINT );
+        io_serv.run();
     }
 
     return 0;