From 8871486142017f8585b687f40a4a622b413cc6d2 Mon Sep 17 00:00:00 2001 From: Guilherme Maciel Ferreira Date: Tue, 3 May 2011 10:10:15 +0200 Subject: [PATCH] working signal handler --- lib/boost-signal_handler/LICENSE_1_0.txt | 23 +++ .../boost/asio/posix/basic_signal_handler.hpp | 62 +++++++ .../asio/posix/basic_signal_handler_service.hpp | 193 ++++++++++++++++++++ .../boost/asio/posix/signal_handler.hpp | 24 +++ .../boost/asio/posix/signal_handler_impl.hpp | 135 ++++++++++++++ src/CMakeLists.txt | 3 + src/host/pingscheduler.cpp | 9 + src/host/pingscheduler.h | 5 +- src/main.cpp | 44 ++++- 9 files changed, 494 insertions(+), 4 deletions(-) create mode 100644 lib/boost-signal_handler/LICENSE_1_0.txt create mode 100644 lib/boost-signal_handler/boost/asio/posix/basic_signal_handler.hpp create mode 100644 lib/boost-signal_handler/boost/asio/posix/basic_signal_handler_service.hpp create mode 100644 lib/boost-signal_handler/boost/asio/posix/signal_handler.hpp create mode 100644 lib/boost-signal_handler/boost/asio/posix/signal_handler_impl.hpp diff --git a/lib/boost-signal_handler/LICENSE_1_0.txt b/lib/boost-signal_handler/LICENSE_1_0.txt new file mode 100644 index 0000000..1dad8e9 --- /dev/null +++ b/lib/boost-signal_handler/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. 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 index 0000000..12602ce --- /dev/null +++ b/lib/boost-signal_handler/boost/asio/posix/basic_signal_handler.hpp @@ -0,0 +1,62 @@ +// +// Copyright (c) 2009, 2010 Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_POSIX_BASIC_SIGNAL_HANDLER_HPP +#define BOOST_ASIO_POSIX_BASIC_SIGNAL_HANDLER_HPP + +#include +#include + +namespace boost { +namespace asio { +namespace posix { + +template +class basic_signal_handler + : public boost::asio::basic_io_object +{ +public: + explicit basic_signal_handler(boost::asio::io_service &io_service) + : boost::asio::basic_io_object(io_service) + { + } + + void add_signal(int signal, int flags = 0, sigset_t *mask = 0) + { + this->service.add_signal(this->implementation, signal, flags, mask); + } + + void remove_signal(int signal) + { + this->service.remove_signal(this->implementation, signal); + } + + int wait() + { + boost::system::error_code ec; + int signal = this->service.wait(this->implementation, ec); + boost::asio::detail::throw_error(ec); + return signal; + } + + int wait(boost::system::error_code &ec) + { + return this->service.wait(this->implementation, ec); + } + + template + void async_wait(Handler handler) + { + this->service.async_wait(this->implementation, handler); + } +}; + +} +} +} + +#endif 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 index 0000000..27c9cd7 --- /dev/null +++ b/lib/boost-signal_handler/boost/asio/posix/basic_signal_handler_service.hpp @@ -0,0 +1,193 @@ +// +// Copyright (c) 2009, 2010 Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_POSIX_BASIC_SIGNAL_HANDLER_SERVICE_HPP +#define BOOST_ASIO_POSIX_BASIC_SIGNAL_HANDLER_SERVICE_HPP + +#include "signal_handler_impl.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace posix { + +template +class basic_signal_handler_service + : public boost::asio::io_service::service +{ +public: + static boost::asio::io_service::id id; + + explicit basic_signal_handler_service(boost::asio::io_service &io_service) + : boost::asio::io_service::service(io_service), + async_wait_work_(new boost::asio::io_service::work(async_wait_io_service_)), + async_wait_thread_(boost::bind(&boost::asio::io_service::run, &async_wait_io_service_)) + { + } + + virtual ~basic_signal_handler_service() + { + // The async_wait thread will finish when async_wait_work_ is reset as all asynchronous + // operations have been aborted and were discarded before (in destroy). + async_wait_work_.reset(); + + // Event processing is stopped to discard queued operations. + async_wait_io_service_.stop(); + + // The async_wait thread is joined to make sure the signal handler service is + // destroyed _after_ the thread is finished (not that the thread tries to access + // instance properties which don't exist anymore). + async_wait_thread_.join(); + } + + typedef boost::shared_ptr implementation_type; + + void construct(implementation_type &impl) + { + // We must count the I/O objects and can't rely on the shared pointer impl_ + // as an asynchronous call can hold a shared pointer at any time, too, thus + // preventing the implementation to be destroyed in destroy(). + ++count_; + + implementation_type shared_impl = impl_.lock(); + if (shared_impl) + { + impl = shared_impl; + } + else + { + impl.reset(new SignalHandlerImplementation()); + impl_ = impl; + fd_ = impl->write_end(); + } + } + + void destroy(implementation_type &impl) + { + // If an asynchronous call is currently waiting for a signal + // we must interrupt the blocked call to make sure it returns. + if (!--count_) + impl->destroy(); + + impl.reset(); + } + + void add_signal(implementation_type &impl, int signal, int flags, sigset_t *mask) + { + struct sigaction act, oldact; + + std::memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = basic_signal_handler_service::signal_handler; + act.sa_flags = flags; + if (mask) + act.sa_mask = *mask; + + int res = sigaction(signal, &act, &oldact); + if (res == -1) + { + 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"); + boost::throw_exception(e); + } + + impl->add_signal(signal, oldact); + } + + void remove_signal(implementation_type &impl, int signal) + { + impl->remove_signal(signal); + } + + int wait(implementation_type &impl, boost::system::error_code &ec) + { + return impl->wait(ec); + } + + template + class wait_operation + { + public: + wait_operation(implementation_type &impl, boost::asio::io_service &io_service, Handler handler) + : impl_(impl), + io_service_(io_service), + work_(io_service), + handler_(handler) + { + } + + void operator()() const + { + implementation_type impl = impl_.lock(); + if (impl) + { + boost::system::error_code ec; + int signal = impl->wait(ec); + this->io_service_.post(boost::asio::detail::bind_handler(handler_, ec, signal)); + } + else + { + this->io_service_.post(boost::asio::detail::bind_handler(handler_, boost::asio::error::operation_aborted, 0)); + } + } + + private: + boost::weak_ptr impl_; + boost::asio::io_service &io_service_; + boost::asio::io_service::work work_; + Handler handler_; + }; + + template + void async_wait(implementation_type &impl, Handler handler) + { + this->async_wait_io_service_.post(wait_operation(impl, this->get_io_service(), handler)); + } + +private: + void shutdown_service() + { + } + + static void signal_handler(int signal) + { + while (::write(fd_, &signal, sizeof(signal)) == -1 && errno == EINTR); + } + + static int count_; + static boost::weak_ptr impl_; + static int fd_; + boost::asio::io_service async_wait_io_service_; + boost::scoped_ptr async_wait_work_; + boost::thread async_wait_thread_; +}; + +template +boost::asio::io_service::id basic_signal_handler_service::id; + +template +int basic_signal_handler_service::count_ = 0; + +template +boost::weak_ptr basic_signal_handler_service::impl_; + +template +int basic_signal_handler_service::fd_; + +} +} +} + +#endif 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 index 0000000..ea6cd1f --- /dev/null +++ b/lib/boost-signal_handler/boost/asio/posix/signal_handler.hpp @@ -0,0 +1,24 @@ +// +// Copyright (c) 2009, 2010 Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_POSIX_SIGNAL_HANDLER_HPP +#define BOOST_ASIO_POSIX_SIGNAL_HANDLER_HPP + +#include "basic_signal_handler.hpp" +#include "basic_signal_handler_service.hpp" + +namespace boost { +namespace asio { +namespace posix { + +typedef basic_signal_handler > signal_handler; + +} +} +} + +#endif 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 index 0000000..084a04b --- /dev/null +++ b/lib/boost-signal_handler/boost/asio/posix/signal_handler_impl.hpp @@ -0,0 +1,135 @@ +// +// Copyright (c) 2009, 2010 Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_POSIX_SIGNAL_HANDLER_IMPL_HPP +#define BOOST_ASIO_POSIX_SIGNAL_HANDLER_IMPL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace posix { + +class signal_handler_impl +{ +public: + signal_handler_impl() + : sd_(signal_handler_io_service_), + run_(true) + { + int res = pipe(fds_); + if (res == -1) + { + 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"); + boost::throw_exception(e); + } + + sd_.assign(fds_[0]); + begin_read(); + signal_handler_thread_ = boost::thread(boost::bind(&boost::asio::io_service::run, &signal_handler_io_service_)); + } + + ~signal_handler_impl() + { + for (sigactions_t::iterator it = sigactions_.begin(); it != sigactions_.end(); ++it) + sigaction(it->first, &it->second, 0); + + close(fds_[1]); + sd_.close(); + + signal_handler_io_service_.stop(); + signal_handler_thread_.join(); + } + + int write_end() const + { + return fds_[1]; + } + + void add_signal(int signal, struct sigaction oldact) + { + sigactions_.insert(sigactions_t::value_type(signal, oldact)); + } + + void remove_signal(int signal) + { + sigactions_t::iterator it = sigactions_.find(signal); + if (it != sigactions_.end()) + { + sigaction(signal, &it->second, 0); + sigactions_.erase(it); + } + } + + void destroy() + { + boost::unique_lock lock(pending_signals_mutex_); + run_ = false; + pending_signals_cond_.notify_all(); + } + + int wait(boost::system::error_code &ec) + { + boost::unique_lock lock(pending_signals_mutex_); + while (run_ && pending_signals_.empty()) + pending_signals_cond_.wait(lock); + int signal = 0; + if (!pending_signals_.empty()) + { + signal = pending_signals_.front(); + pending_signals_.pop_front(); + } + else + ec = boost::asio::error::operation_aborted; + return signal; + } + +private: + void begin_read() + { + boost::asio::async_read(sd_, boost::asio::buffer(signal_buffer_), + boost::bind(&signal_handler_impl::end_read, this, + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void end_read(const boost::system::error_code &ec, std::size_t bytes_transferred) + { + if (!ec) + { + boost::unique_lock lock(pending_signals_mutex_); + pending_signals_.push_back(signal_buffer_[0]); + pending_signals_cond_.notify_all(); + begin_read(); + } + } + + int fds_[2]; + boost::asio::io_service signal_handler_io_service_; + boost::thread signal_handler_thread_; + boost::asio::posix::stream_descriptor sd_; + int signal_buffer_[1]; + typedef std::map sigactions_t; + sigactions_t sigactions_; + boost::mutex pending_signals_mutex_; + boost::condition_variable_any pending_signals_cond_; + bool run_; + std::deque pending_signals_; +}; + +} +} +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d893a3f..a3ff1f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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}) diff --git a/src/host/pingscheduler.cpp b/src/host/pingscheduler.cpp index b04f534..f5252ee 100644 --- a/src/host/pingscheduler.cpp +++ b/src/host/pingscheduler.cpp @@ -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(); diff --git a/src/host/pingscheduler.h b/src/host/pingscheduler.h index be41dab..5337b40 100644 --- a/src/host/pingscheduler.h +++ b/src/host/pingscheduler.h @@ -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(); diff --git a/src/main.cpp b/src/main.cpp index 46f7c09..263678d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,10 @@ +#include + #include #include #include +#include #include #include @@ -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; -- 1.7.1