19ebe83d5SNicolas Bonnefon /* 29ebe83d5SNicolas Bonnefon * Copyright (C) 2015 Nicolas Bonnefon and other contributors 39ebe83d5SNicolas Bonnefon * 49ebe83d5SNicolas Bonnefon * This file is part of glogg. 59ebe83d5SNicolas Bonnefon * 69ebe83d5SNicolas Bonnefon * glogg is free software: you can redistribute it and/or modify 79ebe83d5SNicolas Bonnefon * it under the terms of the GNU General Public License as published by 89ebe83d5SNicolas Bonnefon * the Free Software Foundation, either version 3 of the License, or 99ebe83d5SNicolas Bonnefon * (at your option) any later version. 109ebe83d5SNicolas Bonnefon * 119ebe83d5SNicolas Bonnefon * glogg is distributed in the hope that it will be useful, 129ebe83d5SNicolas Bonnefon * but WITHOUT ANY WARRANTY; without even the implied warranty of 139ebe83d5SNicolas Bonnefon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 149ebe83d5SNicolas Bonnefon * GNU General Public License for more details. 159ebe83d5SNicolas Bonnefon * 169ebe83d5SNicolas Bonnefon * You should have received a copy of the GNU General Public License 179ebe83d5SNicolas Bonnefon * along with glogg. If not, see <http://www.gnu.org/licenses/>. 189ebe83d5SNicolas Bonnefon */ 199ebe83d5SNicolas Bonnefon 20f09fa651SNicolas Bonnefon #ifndef WINWATCHTOWERDRIVER_H 21f09fa651SNicolas Bonnefon #define WINWATCHTOWERDRIVER_H 22f09fa651SNicolas Bonnefon 23f09fa651SNicolas Bonnefon #include <atomic> 24f09fa651SNicolas Bonnefon #include <thread> 25f09fa651SNicolas Bonnefon #include <mutex> 26f09fa651SNicolas Bonnefon #include <condition_variable> 27f09fa651SNicolas Bonnefon #include <iterator> 28f09fa651SNicolas Bonnefon #include <vector> 29f09fa651SNicolas Bonnefon 30f09fa651SNicolas Bonnefon #define WIN32_LEAN_AND_MEAN 31f09fa651SNicolas Bonnefon #include <windows.h> 32f09fa651SNicolas Bonnefon 33f09fa651SNicolas Bonnefon // Utility class 34f09fa651SNicolas Bonnefon 35f09fa651SNicolas Bonnefon // Encapsulate a directory notification returned by Windows' 36f09fa651SNicolas Bonnefon // ReadDirectoryChangesW. 37f09fa651SNicolas Bonnefon class WinNotificationInfo { 38f09fa651SNicolas Bonnefon public: 39f09fa651SNicolas Bonnefon enum class Action { UNDEF, 40f09fa651SNicolas Bonnefon ADDED, 41f09fa651SNicolas Bonnefon REMOVED, 42f09fa651SNicolas Bonnefon MODIFIED, 43f09fa651SNicolas Bonnefon RENAMED_OLD_NAME, 44f09fa651SNicolas Bonnefon RENAMED_NEW_NAME }; 45f09fa651SNicolas Bonnefon WinNotificationInfo()46f09fa651SNicolas Bonnefon WinNotificationInfo() { action_ = Action::UNDEF; } WinNotificationInfo(Action action,std::wstring file_name)47f09fa651SNicolas Bonnefon WinNotificationInfo( Action action, std::wstring file_name ) 48f09fa651SNicolas Bonnefon { action_ = action; file_name_ = file_name; } 49f09fa651SNicolas Bonnefon action()50f09fa651SNicolas Bonnefon Action action() const { return action_; } fileName()51f09fa651SNicolas Bonnefon std::wstring fileName() const { return file_name_; } 52f09fa651SNicolas Bonnefon 53f09fa651SNicolas Bonnefon private: 54f09fa651SNicolas Bonnefon Action action_; 55f09fa651SNicolas Bonnefon std::wstring file_name_; 56f09fa651SNicolas Bonnefon }; 57f09fa651SNicolas Bonnefon 58f09fa651SNicolas Bonnefon class WinNotificationInfoList { 59f09fa651SNicolas Bonnefon public: 60f09fa651SNicolas Bonnefon WinNotificationInfoList( const char* buffer, size_t buffer_size ); 61f09fa651SNicolas Bonnefon 62f09fa651SNicolas Bonnefon // Iterator 63f09fa651SNicolas Bonnefon class iterator : std::iterator<std::input_iterator_tag, WinNotificationInfo> { 64f09fa651SNicolas Bonnefon public: iterator(WinNotificationInfoList * list,const char * position)65f09fa651SNicolas Bonnefon iterator( WinNotificationInfoList* list, const char* position ) 66f09fa651SNicolas Bonnefon { list_ = list; position_ = position; } 67f09fa651SNicolas Bonnefon 68f09fa651SNicolas Bonnefon const WinNotificationInfo& operator*() const 69f09fa651SNicolas Bonnefon { return list_->current_notification_; } 70f09fa651SNicolas Bonnefon 71f09fa651SNicolas Bonnefon const WinNotificationInfo* operator->() const 72f09fa651SNicolas Bonnefon { return &( list_->current_notification_ ); } 73f09fa651SNicolas Bonnefon 74f09fa651SNicolas Bonnefon const WinNotificationInfo& operator++() { 75f09fa651SNicolas Bonnefon position_ = list_->advanceToNext(); 76f09fa651SNicolas Bonnefon return list_->current_notification_; 77f09fa651SNicolas Bonnefon } 78f09fa651SNicolas Bonnefon 79f09fa651SNicolas Bonnefon WinNotificationInfo operator++( int ) { 80f09fa651SNicolas Bonnefon WinNotificationInfo tmp { list_->current_notification_ }; 81f09fa651SNicolas Bonnefon operator++(); 82f09fa651SNicolas Bonnefon return tmp; 83f09fa651SNicolas Bonnefon } 84f09fa651SNicolas Bonnefon 85f09fa651SNicolas Bonnefon bool operator!=( const iterator& other ) const { 86f09fa651SNicolas Bonnefon return ( list_ != other.list_ ) || ( position_ != other.position_ ); 87f09fa651SNicolas Bonnefon } 88f09fa651SNicolas Bonnefon 89f09fa651SNicolas Bonnefon private: 90f09fa651SNicolas Bonnefon WinNotificationInfoList* list_; 91f09fa651SNicolas Bonnefon const char* position_; 92f09fa651SNicolas Bonnefon }; 93f09fa651SNicolas Bonnefon begin()94f09fa651SNicolas Bonnefon iterator begin() { return iterator( this, pointer_ ); } end()95f09fa651SNicolas Bonnefon iterator end() { return iterator( this, nullptr ); } 96f09fa651SNicolas Bonnefon 97f09fa651SNicolas Bonnefon private: 98f09fa651SNicolas Bonnefon const char* advanceToNext(); 99f09fa651SNicolas Bonnefon 100f09fa651SNicolas Bonnefon // Current notification (in the byte stream) 101f09fa651SNicolas Bonnefon const char* pointer_; 102f09fa651SNicolas Bonnefon // Next notification (in the byte stream) 103f09fa651SNicolas Bonnefon const char* next_; 104f09fa651SNicolas Bonnefon WinNotificationInfo current_notification_; 105f09fa651SNicolas Bonnefon 106f09fa651SNicolas Bonnefon const char* updateCurrentNotification( const char* new_position ); 107f09fa651SNicolas Bonnefon }; 108f09fa651SNicolas Bonnefon 109f09fa651SNicolas Bonnefon template <typename Driver> 110f09fa651SNicolas Bonnefon class ObservedFile; 111f09fa651SNicolas Bonnefon template <typename Driver> 112f09fa651SNicolas Bonnefon class ObservedFileList; 113f09fa651SNicolas Bonnefon 114f09fa651SNicolas Bonnefon class WinWatchTowerDriver { 115f09fa651SNicolas Bonnefon public: 116f09fa651SNicolas Bonnefon struct WinWatchedDirRecord { WinWatchedDirRecordWinWatchedDirRecord117f09fa651SNicolas Bonnefon WinWatchedDirRecord( const std::string& file_name ) 118f09fa651SNicolas Bonnefon : path_( file_name ) { } 119f09fa651SNicolas Bonnefon 120f09fa651SNicolas Bonnefon static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096; 121f09fa651SNicolas Bonnefon 122f09fa651SNicolas Bonnefon std::string path_; 123f09fa651SNicolas Bonnefon void* handle_ = nullptr; 124f09fa651SNicolas Bonnefon static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE; 125f09fa651SNicolas Bonnefon char buffer_[buffer_length_]; 126f09fa651SNicolas Bonnefon }; 127f09fa651SNicolas Bonnefon 128f09fa651SNicolas Bonnefon class FileId { }; 129f09fa651SNicolas Bonnefon class SymlinkId { }; 130f09fa651SNicolas Bonnefon class DirId { 131f09fa651SNicolas Bonnefon public: 132f09fa651SNicolas Bonnefon friend class WinWatchTowerDriver; 133f09fa651SNicolas Bonnefon DirId()134f09fa651SNicolas Bonnefon DirId() {} 135f09fa651SNicolas Bonnefon bool operator==( const DirId& other ) const 136f09fa651SNicolas Bonnefon { return dir_record_ == other.dir_record_; } valid()1378b11848fSNicolas Bonnefon bool valid() const 1388b11848fSNicolas Bonnefon { return ( dir_record_ != nullptr ); } 139f09fa651SNicolas Bonnefon private: 140f09fa651SNicolas Bonnefon std::shared_ptr<WinWatchedDirRecord> dir_record_; 141f09fa651SNicolas Bonnefon }; 142f09fa651SNicolas Bonnefon 143fcaa7557SNicolas Bonnefon // On Windows, the token is the "last write" time 144fcaa7557SNicolas Bonnefon class FileChangeToken { 145fcaa7557SNicolas Bonnefon public: FileChangeToken()146fcaa7557SNicolas Bonnefon FileChangeToken() {} FileChangeToken(const std::string & file_name)147fcaa7557SNicolas Bonnefon FileChangeToken( const std::string& file_name ) 148fcaa7557SNicolas Bonnefon { readFromFile( file_name ); } 149fcaa7557SNicolas Bonnefon 150fcaa7557SNicolas Bonnefon void readFromFile( const std::string& file_name ); 151fcaa7557SNicolas Bonnefon 152fcaa7557SNicolas Bonnefon bool operator==( const FileChangeToken& other ) 153fcaa7557SNicolas Bonnefon { return ( low_date_time_ == other.low_date_time_ ) && 154*f7ea9867SNicolas Bonnefon ( high_date_time_ == other.high_date_time_ ) && 155*f7ea9867SNicolas Bonnefon ( low_file_size_ == other.low_file_size_ ) && 156*f7ea9867SNicolas Bonnefon ( high_file_size_ == other.high_file_size_); } 157fcaa7557SNicolas Bonnefon bool operator!=( const FileChangeToken& other ) 158fcaa7557SNicolas Bonnefon { return ! operator==( other ); } 159fcaa7557SNicolas Bonnefon 160fcaa7557SNicolas Bonnefon private: 161fcaa7557SNicolas Bonnefon DWORD low_date_time_ = 0; 162fcaa7557SNicolas Bonnefon DWORD high_date_time_ = 0; 163*f7ea9867SNicolas Bonnefon DWORD low_file_size_ = 0; 164*f7ea9867SNicolas Bonnefon DWORD high_file_size_ = 0; 165fcaa7557SNicolas Bonnefon }; 166fcaa7557SNicolas Bonnefon 167f09fa651SNicolas Bonnefon // Default constructor 168f09fa651SNicolas Bonnefon WinWatchTowerDriver(); 169f09fa651SNicolas Bonnefon ~WinWatchTowerDriver(); 170f09fa651SNicolas Bonnefon 171f09fa651SNicolas Bonnefon FileId addFile( const std::string& file_name ); 172f09fa651SNicolas Bonnefon SymlinkId addSymlink( const std::string& file_name ); 173f09fa651SNicolas Bonnefon DirId addDir( const std::string& file_name ); 174f09fa651SNicolas Bonnefon 175f09fa651SNicolas Bonnefon void removeFile( const FileId& file_id ); 176f09fa651SNicolas Bonnefon void removeSymlink( const SymlinkId& symlink_id ); 1773104b268SNicolas Bonnefon void removeDir( const DirId& dir_id ); 178f09fa651SNicolas Bonnefon 179f09fa651SNicolas Bonnefon std::vector<ObservedFile<WinWatchTowerDriver>*> waitAndProcessEvents( 180f09fa651SNicolas Bonnefon ObservedFileList<WinWatchTowerDriver>* list, 18191f7c705SNicolas Bonnefon std::unique_lock<std::mutex>* lock, 182fcaa7557SNicolas Bonnefon std::vector<ObservedFile<WinWatchTowerDriver>*>* files_needing_readding, 183fcaa7557SNicolas Bonnefon int timout_ms ); 184f09fa651SNicolas Bonnefon 1859ebe83d5SNicolas Bonnefon // Interrupt waitAndProcessEvents 186f09fa651SNicolas Bonnefon void interruptWait(); 187f09fa651SNicolas Bonnefon 188f09fa651SNicolas Bonnefon private: 189f09fa651SNicolas Bonnefon // An action which will be picked up by the worker thread. 190f09fa651SNicolas Bonnefon class Action { 191f09fa651SNicolas Bonnefon public: Action(std::function<void ()> function)192f09fa651SNicolas Bonnefon Action( std::function<void()> function ) : function_ { function } {} ~Action()193f09fa651SNicolas Bonnefon ~Action() {} 194f09fa651SNicolas Bonnefon operator()195f09fa651SNicolas Bonnefon void operator()() { function_(); } 196f09fa651SNicolas Bonnefon 197f09fa651SNicolas Bonnefon private: 198f09fa651SNicolas Bonnefon std::function<void()> function_; 199f09fa651SNicolas Bonnefon }; 200f09fa651SNicolas Bonnefon 201f09fa651SNicolas Bonnefon // Action 202f09fa651SNicolas Bonnefon std::mutex action_mutex_; 203f09fa651SNicolas Bonnefon std::condition_variable action_done_cv_; 204f09fa651SNicolas Bonnefon std::unique_ptr<Action> scheduled_action_ = nullptr; 205f09fa651SNicolas Bonnefon 206f09fa651SNicolas Bonnefon // Win32 notification variables 207f09fa651SNicolas Bonnefon HANDLE hCompPort_; 208f09fa651SNicolas Bonnefon OVERLAPPED overlapped_; 209f09fa651SNicolas Bonnefon unsigned long buffer_length_; 210f09fa651SNicolas Bonnefon 211f09fa651SNicolas Bonnefon // List of directory records 212f09fa651SNicolas Bonnefon // Accessed exclusively in the worker thread context 213f09fa651SNicolas Bonnefon std::vector<std::weak_ptr<WinWatchedDirRecord>> dir_records_ { }; 214f09fa651SNicolas Bonnefon 215f09fa651SNicolas Bonnefon // Private member functions 216f09fa651SNicolas Bonnefon void serialisedAddDir( 217f09fa651SNicolas Bonnefon const std::string& file_name, 218f09fa651SNicolas Bonnefon DirId& dir_id ); 219f09fa651SNicolas Bonnefon }; 220f09fa651SNicolas Bonnefon 221f09fa651SNicolas Bonnefon #endif 222