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-2018, 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 "system-precomp.h" // Precompiled headers
11 
12 #include <mrpt/config.h>
13 
14 #ifdef _WIN32
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/core/exceptions.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 _WIN32
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 
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 _WIN32
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 _WIN32
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_msgs.empty())
129  {
130  TFileSystemChange* obj = nullptr;
131  {
132  std::lock_guard<std::mutex> lock(m_queue_events_win32_cs);
133  if (!m_queue_events_win32_msgs.empty())
134  {
135  auto ret = m_queue_events_win32_msgs.front();
137  obj = ret;
138  }
139  }
140  if (obj)
141  {
142  out_list.push_back(*obj);
143  delete obj;
144  }
145  }
146 
147 #else
148 #if MRPT_HAS_INOTIFY
149  if (m_fd < 0) return; // Not open?
150 
151  // Linux version:
152  // Refer to:
153  // http://www.linuxjournal.com/article/8478
154  // http://inotify.aiken.cz/?section=common&page=home&lang=en
155  struct timeval time;
156  fd_set rfds;
157  int ret;
158 
159  // timeout
160  time.tv_sec = 0;
161  time.tv_usec = 100;
162 
163  // zero-out the fd_set
164  FD_ZERO(&rfds);
165 
166  // Add inotify fd
167  FD_SET(m_fd, &rfds);
168 
169  ret = select(m_fd + 1, &rfds, nullptr, nullptr, &time);
170  if (ret < 0)
171  {
172  perror("[CFileSystemWatcher::getChanges] select");
173  return;
174  }
175 
176  else if (!ret)
177  {
178  // timed out!
179  }
180  else if (FD_ISSET(m_fd, &rfds))
181  {
182 // inotify events are available! : Read them!
183 
184 /* size of the event structure, not counting name */
185 #define EVENT_SIZE (sizeof(struct inotify_event))
186 
187 /* reasonable guess as to size of 1024 events */
188 #define BUF_LEN (1024 * (EVENT_SIZE + 16))
189 
190  char buf[BUF_LEN];
191  int len, i = 0;
192 
193  len = read(m_fd, buf, BUF_LEN);
194  if (len < 0)
195  {
196  if (errno == EINTR)
197  {
198  /* need to reissue system call */
199  }
200  else
201  perror("[CFileSystemWatcher::getChanges] read");
202  }
203  else if (!len)
204  {
205  /* BUF_LEN too small? */
206  }
207 
208  while (i < len)
209  {
210  struct inotify_event event_val;
211  ::memcpy(
212  &event_val, &buf[i],
213  sizeof(event_val)); // Was: event = (struct inotify_event *) ;
214  struct inotify_event* event = &event_val;
215 
216  i += EVENT_SIZE + event->len;
217 
218  // printf ("wd=%d mask=%u cookie=%u len=%u\n",event->wd,
219  // event->mask,event->cookie, event->len);
220 
221  string eventName;
222  if (event->len) eventName = event->name;
223 
224  // Add event to output list:
225  // ---------------------------------------------
226  if (0 == (event->mask & IN_UNMOUNT) &&
227  0 == (event->mask & IN_Q_OVERFLOW) &&
228  0 == (event->mask & IN_IGNORED))
229  {
230  TFileSystemChange newEntry;
231 
232  newEntry.path = m_watchedDirectory + eventName;
233  newEntry.isDir = event->mask & IN_ISDIR;
234  newEntry.eventModified = event->mask & IN_MODIFY;
235  newEntry.eventCloseWrite = event->mask & IN_CLOSE_WRITE;
236  newEntry.eventDeleted = event->mask & IN_DELETE;
237  newEntry.eventMovedTo = event->mask & IN_MOVED_TO;
238  newEntry.eventMovedFrom = event->mask & IN_MOVED_FROM;
239  newEntry.eventCreated = event->mask & IN_CREATE;
240  newEntry.eventAccessed = event->mask & IN_ACCESS;
241 
242  out_list.push_back(newEntry);
243  }
244  }
245  }
246 #endif
247 #endif
248 }
249 
250 #ifdef _WIN32
251 
253 {
254  uint8_t buf[8 * 1024];
255  DWORD dwRead = 0;
256 
257  while (ReadDirectoryChangesW(
258  HANDLE(m_hNotif), buf, sizeof(buf),
259  false, // No subtree
260  FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
261  FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE |
262  FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS |
263  FILE_NOTIFY_CHANGE_CREATION,
264  &dwRead, nullptr, nullptr))
265  {
266  // Interpret read data as FILE_NOTIFY_INFORMATION:
267  // There might be several notifications in the same data block:
268  size_t idx = 0;
269  for (;;)
270  {
271  // Yep... this is MS's idea of a beautiful and easy way to return
272  // data:
273  FILE_NOTIFY_INFORMATION* fni =
274  reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buf[idx]);
275 
276  // Extract the name (stored as a WCHAR*) into a UTF-8 std::string:
277  ASSERTMSG_(
278  fni->FileNameLength < 10000,
279  "Name length >10K... this is probably an error");
280 
281  int reqLen = WideCharToMultiByte(
282  CP_UTF8, 0, fni->FileName, fni->FileNameLength >> 1, nullptr, 0,
283  nullptr, nullptr);
284  std::vector<char> tmpBuf(reqLen);
285  int actLen = WideCharToMultiByte(
286  CP_UTF8, 0, fni->FileName, fni->FileNameLength >> 1, &tmpBuf[0],
287  tmpBuf.size(), nullptr, nullptr);
288  ASSERTMSG_(
289  actLen > 0, "Error converting filename from WCHAR* to UTF8");
290 
291  const std::string filName(&tmpBuf[0], actLen);
292 
293  TFileSystemChange newEntry;
294  newEntry.path = m_watchedDirectory + filName;
295  newEntry.isDir = mrpt::system::directoryExists(newEntry.path);
296 
297  // Fill out the new entry:
298  std::lock_guard<std::mutex> lock(m_queue_events_win32_cs);
299  switch (fni->Action)
300  {
301  case FILE_ACTION_ADDED:
302  {
303  newEntry.eventCreated = true;
305  new TFileSystemChange(newEntry));
306  }
307  break;
308  case FILE_ACTION_REMOVED:
309  {
310  newEntry.eventDeleted = true;
312  new TFileSystemChange(newEntry));
313  }
314  break;
315  case FILE_ACTION_MODIFIED:
316  {
317  newEntry.eventModified = true;
319  new TFileSystemChange(newEntry));
320  }
321  break;
322  case FILE_ACTION_RENAMED_OLD_NAME:
323  {
324  newEntry.eventMovedFrom = true;
326  new TFileSystemChange(newEntry));
327  }
328  break;
329  case FILE_ACTION_RENAMED_NEW_NAME:
330  {
331  newEntry.eventMovedTo = true;
333  new TFileSystemChange(newEntry));
334  }
335  break;
336  }
337 
338  // Next entry?
339  if (fni->NextEntryOffset > 0)
340  idx += fni->NextEntryOffset;
341  else
342  break; // done
343  }
344  }
345 
346  printf("Done!");
347 }
348 #endif
#define MRPT_START
Definition: exceptions.h:262
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:41
std::queue< TFileSystemChange * > m_queue_events_win32_msgs
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.
std::deque< TFileSystemChange > TFileSystemChangeList
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 ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:113
void thread_win32_watch()
Watch thread; only needed in win32.
#define ASSERTMSG_(f, __ERROR_MSG)
Defines an assertion mechanism.
Definition: exceptions.h:101
GLsizei const GLchar ** string
Definition: glext.h:4101
bool isDir
Whether the event happened to a file or a directory.
virtual ~CFileSystemWatcher()
Destructor.
#define MRPT_END
Definition: exceptions.h:266
Each of the changes detected by utils::CFileSystemWatcher.
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
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:356
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: 7d5e6d718 Fri Aug 24 01:51:28 2018 +0200 at lun nov 2 08:35:50 CET 2020