Main MRPT website > C++ reference for MRPT 1.9.9
CFileSystemWatcher.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "base-precomp.h" // Precompiled headers
11 
12 #include <mrpt/config.h>
13 
14 #ifdef MRPT_OS_WINDOWS
15 #ifndef _WIN32_WINNT
16 #define _WIN32_WINNT 0x0400
17 #endif
18 #include <windows.h>
19 #else
20 #if MRPT_HAS_INOTIFY
21 #include <sys/inotify.h>
22 #endif
23 
24 // #include <time.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #endif
29 
30 #include <cstring> // for NULL, memcpy
32 #include <mrpt/system/filesystem.h> // for directoryExists()
33 #include <mrpt/utils/mrpt_macros.h> // for THROW_EXCEPTION, ASSERT_
34 
35 using namespace mrpt::system;
36 using namespace std;
37 
38 /*---------------------------------------------------------------
39  Constructor
40  ---------------------------------------------------------------*/
42  : m_watchedDirectory(path)
43 {
45  ASSERT_(!path.empty())
46 
47  if (m_watchedDirectory[m_watchedDirectory.size() - 1] != '/' &&
48  m_watchedDirectory[m_watchedDirectory.size() - 1] != '\\')
49  m_watchedDirectory.push_back('/');
50 
51 #ifdef MRPT_OS_WINDOWS
52  // Windows version:
53  HANDLE hDir = CreateFileA(
54  path.c_str(), FILE_LIST_DIRECTORY,
55  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
56  nullptr, // security attributes
57  OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
58  if (hDir == INVALID_HANDLE_VALUE)
59  {
60  m_hNotif = nullptr;
61  THROW_EXCEPTION("FindFirstChangeNotificationA returned error!");
62  }
63 
64  m_hNotif = static_cast<void*>(hDir);
65 
66  m_watchThread = std::thread(&CFileSystemWatcher::thread_win32_watch, this);
67 
68 #else
69 #if MRPT_HAS_INOTIFY
70  // Linux version:
71  m_wd = -1;
72 
73  m_fd = inotify_init();
74  if (m_fd < 0) THROW_EXCEPTION("inotify_init returned error!");
75 
76  // Create watcher:
77  m_wd = inotify_add_watch(
78  m_fd, path.c_str(), IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO |
79  IN_MOVED_FROM | IN_CREATE | IN_ACCESS);
80 
81  if (m_wd < 0) THROW_EXCEPTION("inotify_add_watch returned error!");
82 #endif
83 #endif
84  MRPT_END
85 }
86 
87 /*---------------------------------------------------------------
88  Destructor
89  ---------------------------------------------------------------*/
91 {
92 #ifdef MRPT_OS_WINDOWS
93  // Windows version:
94  if (m_hNotif)
95  {
96  // Kill thread:
97  CloseHandle(HANDLE(m_hNotif));
98  m_hNotif = nullptr;
99  }
100 #else
101 #if MRPT_HAS_INOTIFY
102  // Linux version:
103  if (m_fd >= 0)
104  {
105  close(m_fd);
106  m_fd = -1;
107  if (m_wd >= 0) inotify_rm_watch(m_fd, m_wd);
108  }
109 #endif
110 #endif
111 }
112 
113 /*---------------------------------------------------------------
114  getChanges
115  ---------------------------------------------------------------*/
117 {
118  out_list.clear();
119 
120 #ifdef MRPT_OS_WINDOWS
121  // Windows version:
122  ASSERTMSG_(
123  m_hNotif != nullptr,
124  "CFileSystemWatcher was not initialized correctly.")
125 
126  // Work is done in thread_win32_watch().
127  // Just check for incoming mail:
128  while (!m_queue_events_win32.empty())
129  {
130  TFileSystemChange* obj = m_queue_events_win32.get();
131  if (obj)
132  {
133  out_list.push_back(*obj);
134  delete obj;
135  }
136  }
137 
138 #else
139 #if MRPT_HAS_INOTIFY
140  if (m_fd < 0) return; // Not open?
141 
142  // Linux version:
143  // Refer to:
144  // http://www.linuxjournal.com/article/8478
145  // http://inotify.aiken.cz/?section=common&page=home&lang=en
146  struct timeval time;
147  fd_set rfds;
148  int ret;
149 
150  // timeout
151  time.tv_sec = 0;
152  time.tv_usec = 100;
153 
154  // zero-out the fd_set
155  FD_ZERO(&rfds);
156 
157  // Add inotify fd
158  FD_SET(m_fd, &rfds);
159 
160  ret = select(m_fd + 1, &rfds, nullptr, nullptr, &time);
161  if (ret < 0)
162  {
163  perror("[CFileSystemWatcher::getChanges] select");
164  return;
165  }
166 
167  else if (!ret)
168  {
169  // timed out!
170  }
171  else if (FD_ISSET(m_fd, &rfds))
172  {
173 // inotify events are available! : Read them!
174 
175 /* size of the event structure, not counting name */
176 #define EVENT_SIZE (sizeof(struct inotify_event))
177 
178 /* reasonable guess as to size of 1024 events */
179 #define BUF_LEN (1024 * (EVENT_SIZE + 16))
180 
181  char buf[BUF_LEN];
182  int len, i = 0;
183 
184  len = read(m_fd, buf, BUF_LEN);
185  if (len < 0)
186  {
187  if (errno == EINTR)
188  {
189  /* need to reissue system call */
190  }
191  else
192  perror("[CFileSystemWatcher::getChanges] read");
193  }
194  else if (!len)
195  {
196  /* BUF_LEN too small? */
197  }
198 
199  while (i < len)
200  {
201  struct inotify_event event_val;
202  ::memcpy(
203  &event_val, &buf[i],
204  sizeof(event_val)); // Was: event = (struct inotify_event *) ;
205  struct inotify_event* event = &event_val;
206 
207  i += EVENT_SIZE + event->len;
208 
209  // printf ("wd=%d mask=%u cookie=%u len=%u\n",event->wd,
210  // event->mask,event->cookie, event->len);
211 
212  string eventName;
213  if (event->len) eventName = event->name;
214 
215  // Add event to output list:
216  // ---------------------------------------------
217  if (0 == (event->mask & IN_UNMOUNT) &&
218  0 == (event->mask & IN_Q_OVERFLOW) &&
219  0 == (event->mask & IN_IGNORED))
220  {
221  TFileSystemChange newEntry;
222 
223  newEntry.path = m_watchedDirectory + eventName;
224  newEntry.isDir = event->mask & IN_ISDIR;
225  newEntry.eventModified = event->mask & IN_MODIFY;
226  newEntry.eventCloseWrite = event->mask & IN_CLOSE_WRITE;
227  newEntry.eventDeleted = event->mask & IN_DELETE;
228  newEntry.eventMovedTo = event->mask & IN_MOVED_TO;
229  newEntry.eventMovedFrom = event->mask & IN_MOVED_FROM;
230  newEntry.eventCreated = event->mask & IN_CREATE;
231  newEntry.eventAccessed = event->mask & IN_ACCESS;
232 
233  out_list.push_back(newEntry);
234  }
235  }
236  }
237 #endif
238 #endif
239 }
240 
241 #ifdef MRPT_OS_WINDOWS
242 
243 void CFileSystemWatcher::thread_win32_watch()
244 {
245  uint8_t buf[8 * 1024];
246  DWORD dwRead = 0;
247 
248  while (ReadDirectoryChangesW(
249  HANDLE(m_hNotif), buf, sizeof(buf),
250  false, // No subtree
251  FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
252  FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE |
253  FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS |
254  FILE_NOTIFY_CHANGE_CREATION,
255  &dwRead, nullptr, nullptr))
256  {
257  // Interpret read data as FILE_NOTIFY_INFORMATION:
258  // There might be several notifications in the same data block:
259  size_t idx = 0;
260  for (;;)
261  {
262  // Yep... this is MS's idea of a beautiful and easy way to return
263  // data:
264  FILE_NOTIFY_INFORMATION* fni =
265  reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buf[idx]);
266 
267  // Extract the name (stored as a WCHAR*) into a UTF-8 std::string:
268  ASSERTMSG_(
269  fni->FileNameLength < 10000,
270  "Name length >10K... this is probably an error")
271 
272  int reqLen = WideCharToMultiByte(
273  CP_UTF8, 0, fni->FileName, fni->FileNameLength >> 1, nullptr, 0,
274  nullptr, nullptr);
275  std::vector<char> tmpBuf(reqLen);
276  int actLen = WideCharToMultiByte(
277  CP_UTF8, 0, fni->FileName, fni->FileNameLength >> 1, &tmpBuf[0],
278  tmpBuf.size(), nullptr, nullptr);
279  ASSERTMSG_(
280  actLen > 0, "Error converting filename from WCHAR* to UTF8")
281 
282  const std::string filName(&tmpBuf[0], actLen);
283 
284  TFileSystemChange newEntry;
285  newEntry.path = m_watchedDirectory + filName;
286  newEntry.isDir = mrpt::system::directoryExists(newEntry.path);
287 
288  // Fill out the new entry:
289  switch (fni->Action)
290  {
291  case FILE_ACTION_ADDED:
292  {
293  newEntry.eventCreated = true;
294  m_queue_events_win32.push(new TFileSystemChange(newEntry));
295  }
296  break;
297  case FILE_ACTION_REMOVED:
298  {
299  newEntry.eventDeleted = true;
300  m_queue_events_win32.push(new TFileSystemChange(newEntry));
301  }
302  break;
303  case FILE_ACTION_MODIFIED:
304  {
305  newEntry.eventModified = true;
306  m_queue_events_win32.push(new TFileSystemChange(newEntry));
307  }
308  break;
309  case FILE_ACTION_RENAMED_OLD_NAME:
310  {
311  newEntry.eventMovedFrom = true;
312  m_queue_events_win32.push(new TFileSystemChange(newEntry));
313  }
314  break;
315  case FILE_ACTION_RENAMED_NEW_NAME:
316  {
317  newEntry.eventMovedTo = true;
318  m_queue_events_win32.push(new TFileSystemChange(newEntry));
319  }
320  break;
321  }
322 
323  // Next entry?
324  if (fni->NextEntryOffset > 0)
325  idx += fni->NextEntryOffset;
326  else
327  break; // done
328  }
329  }
330 
331  printf("Done!");
332 }
333 #endif
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:30
#define THROW_EXCEPTION(msg)
std::string path
Complete path of the file/directory that has changed.
STL namespace.
void getChanges(TFileSystemChangeList &out_list)
Call this method sometimes to get the list of changes in the watched directory.
GLenum GLsizei len
Definition: glext.h:4712
GLsizei GLsizei GLuint * obj
Definition: glext.h:4070
std::string m_watchedDirectory
Ended in "/".
unsigned char uint8_t
Definition: rptypes.h:41
#define MRPT_END
std::deque< TFileSystemChange > TFileSystemChangeList
GLsizei const GLchar ** string
Definition: glext.h:4101
#define MRPT_START
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
bool isDir
Whether the event happened to a file or a directory.
virtual ~CFileSystemWatcher()
Destructor.
#define ASSERT_(f)
Each of the changes detected by utils::CFileSystemWatcher.
GLsizeiptr size
Definition: glext.h:3923
bool directoryExists(const std::string &fileName)
Test if a given directory exists (it fails if the given path refers to an existing file)...
Definition: filesystem.cpp:136
#define ASSERTMSG_(f, __ERROR_MSG)
void memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) noexcept
An OS and compiler independent version of "memcpy".
Definition: os.cpp:355
CFileSystemWatcher(const std::string &path)
Creates the subscription to a specified path.



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019