├── README.md ├── file_monitor.hpp ├── file_monitor_event.hpp ├── basic_file_monitor.hpp ├── file_monitor_service.hpp └── inotify └── file_monitor_service.hpp /README.md: -------------------------------------------------------------------------------- 1 | file-monitor-service 2 | ==================== 3 | 4 | A service that can be used with Boost.Asio to asynchronously monitor file events. -------------------------------------------------------------------------------- /file_monitor.hpp: -------------------------------------------------------------------------------- 1 | #include "basic_file_monitor.hpp" 2 | #include "file_monitor_service.hpp" 3 | 4 | namespace services 5 | { 6 | typedef basic_file_monitor< file_monitor_service > file_monitor; 7 | } 8 | -------------------------------------------------------------------------------- /file_monitor_event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace services 6 | { 7 | struct event 8 | { 9 | enum event_type 10 | { 11 | null = 0, 12 | access, 13 | attrib, 14 | close_write, 15 | close_nowrite, 16 | modify, 17 | delete_self, 18 | move_self, 19 | open, 20 | }; 21 | 22 | event_type type; 23 | std::string filename; 24 | 25 | event ( const std::string& f = "", event_type t = event::null ) : 26 | filename(f), type(t) 27 | { 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /basic_file_monitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace services 10 | { 11 | template 12 | class basic_file_monitor : 13 | public boost::asio::basic_io_object< Service > 14 | { 15 | public: 16 | 17 | explicit basic_file_monitor ( boost::asio::io_service& io_service ) : 18 | boost::asio::basic_io_object< Service >( io_service ) 19 | { 20 | } 21 | 22 | void add_file ( const std::string& filename ) 23 | { 24 | boost::system::error_code ec; 25 | this->service.add_file ( this->implementation, filename, ec ); 26 | boost::asio::detail::throw_error( ec, "add_file" ); 27 | } 28 | 29 | template 30 | void async_monitor( BOOST_ASIO_MOVE_ARG( MonHandler ) handler ) 31 | { 32 | boost::system::error_code ec; 33 | this->service.async_monitor ( this->implementation, ec, BOOST_ASIO_MOVE_CAST(MonHandler)(handler) ); 34 | boost::asio::detail::throw_error( ec, "async_monitor" ); 35 | } 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /file_monitor_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) 7 | # include "inotify/file_monitor_service.hpp" 8 | #else 9 | # error "Platform not supported." 10 | #endif 11 | 12 | namespace services 13 | { 14 | class file_monitor_service : 15 | public boost::asio::io_service::service 16 | { 17 | private: 18 | typedef detail::file_monitor_service service_impl_type; 19 | public: 20 | typedef service_impl_type::implementation_type implementation_type; 21 | 22 | static boost::asio::io_service::id id; 23 | 24 | explicit file_monitor_service ( boost::asio::io_service& io_service ) : 25 | boost::asio::io_service::service( io_service ), 26 | service_impl_( io_service ) 27 | { 28 | } 29 | 30 | void construct( implementation_type& impl ) 31 | { 32 | service_impl_.construct( impl ); 33 | } 34 | 35 | void destroy( implementation_type& impl ) 36 | { 37 | service_impl_.destroy( impl ); 38 | } 39 | 40 | void add_file ( implementation_type& impl, const std::string& file, boost::system::error_code& ec ) 41 | { 42 | service_impl_.add_file( impl, file, ec ); 43 | } 44 | 45 | template 46 | void async_monitor ( implementation_type& impl, boost::system::error_code& ec, BOOST_ASIO_MOVE_ARG( MonHandler ) handler ) 47 | { 48 | service_impl_.async_monitor( impl, ec, BOOST_ASIO_MOVE_CAST(MonHandler)(handler) ); 49 | } 50 | 51 | private: 52 | void shutdown_service() 53 | { 54 | service_impl_.shutdown_service(); 55 | } 56 | 57 | private: 58 | service_impl_type service_impl_; 59 | }; 60 | 61 | boost::asio::io_service::id file_monitor_service::id; 62 | } 63 | -------------------------------------------------------------------------------- /inotify/file_monitor_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../file_monitor_event.hpp" 19 | 20 | namespace services { namespace detail { 21 | 22 | class file_monitor_service 23 | { 24 | public: 25 | class implementation_type : 26 | private boost::asio::detail::noncopyable 27 | { 28 | int fd_; 29 | std::map< int, std::string > watched_files_; 30 | boost::shared_ptr< boost::asio::posix::stream_descriptor > input_; 31 | boost::array buffer_; 32 | std::string buffer_str_; 33 | 34 | friend class file_monitor_service; 35 | }; 36 | 37 | explicit file_monitor_service ( boost::asio::io_service& io_service ) : 38 | io_service_( io_service ) 39 | { 40 | } 41 | 42 | void shutdown_service () 43 | { 44 | } 45 | 46 | void construct( implementation_type& impl ) 47 | { 48 | impl.fd_ = init_fd(); 49 | impl.input_.reset( new boost::asio::posix::stream_descriptor( io_service_, impl.fd_ ) ); 50 | } 51 | 52 | void destroy( implementation_type& impl ) 53 | { 54 | impl.input_.reset(); 55 | } 56 | 57 | void add_file ( implementation_type& impl, const std::string& file, boost::system::error_code& ec ) 58 | { 59 | int wd = inotify_add_watch( impl.fd_, file.c_str(), IN_ALL_EVENTS ); 60 | 61 | if (wd == -1) 62 | { 63 | ec = boost::system::error_code(errno, boost::system::get_system_category()); 64 | } 65 | else if( impl.watched_files_.find( wd ) == impl.watched_files_.end() ) 66 | { 67 | impl.watched_files_[wd] = file; 68 | } 69 | } 70 | 71 | template 72 | void async_monitor ( implementation_type& impl, boost::system::error_code& ec, MonHandler handler ) 73 | { 74 | impl.input_->async_read_some 75 | ( 76 | boost::asio::buffer( impl.buffer_ ), 77 | boost::bind 78 | ( 79 | &file_monitor_service::handle_monitor, this, boost::ref( impl ), 80 | boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, handler 81 | ) 82 | ); 83 | } 84 | 85 | private: 86 | template 87 | void handle_monitor ( implementation_type& impl, boost::system::error_code ec, std::size_t bytes_transferred, MonHandler handler ) 88 | { 89 | if( !ec ) 90 | { 91 | impl.buffer_str_.append( impl.buffer_.data(), impl.buffer_.data() + bytes_transferred ); 92 | 93 | while( impl.buffer_str_.size() >= sizeof( inotify_event ) ) 94 | { 95 | const inotify_event *iev = reinterpret_cast( impl.buffer_str_.data() ); 96 | events_t::const_iterator event_i = events.find( iev->mask ); 97 | if( event_i != events.end() ) 98 | { 99 | io_service_.post 100 | ( 101 | boost::asio::detail::bind_handler 102 | ( 103 | handler, ec, 104 | event 105 | ( 106 | impl.watched_files_[iev->wd], 107 | event_i->second 108 | ) 109 | ) 110 | ); 111 | } 112 | impl.buffer_str_.erase( 0, sizeof( inotify_event ) + iev->len ); 113 | } 114 | async_monitor( impl, ec, handler ); 115 | } 116 | } 117 | 118 | int init_fd () 119 | { 120 | int fd = inotify_init1( IN_NONBLOCK ); 121 | if ( fd == -1 ) 122 | { 123 | boost::system::system_error e 124 | ( 125 | boost::system::error_code( errno, boost::system::get_system_category() ), 126 | "file_monitor_service::init_fd: init_inotify failed" 127 | ); 128 | boost::throw_exception(e); 129 | } 130 | return fd; 131 | } 132 | 133 | boost::asio::io_service& io_service_; 134 | 135 | typedef std::map< int, event::event_type > events_t; 136 | static const events_t events; 137 | }; 138 | 139 | const file_monitor_service::events_t file_monitor_service::events = 140 | boost::assign::map_list_of 141 | ( IN_ACCESS, event::access ) 142 | ( IN_ATTRIB, event::attrib ) 143 | ( IN_CLOSE_WRITE, event::close_write ) 144 | ( IN_CLOSE_NOWRITE, event::close_nowrite ) 145 | ( IN_MODIFY, event::modify ) 146 | ( IN_DELETE_SELF, event::delete_self ) 147 | ( IN_MOVE_SELF, event::move_self ) 148 | ( IN_OPEN, event::open ); 149 | } } 150 | --------------------------------------------------------------------------------