Main MRPT website > C++ reference for MRPT 1.5.7
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] != '/' && m_watchedDirectory[m_watchedDirectory.size()-1] != '\\' )
48  m_watchedDirectory.push_back('/');
49 
50 #ifdef MRPT_OS_WINDOWS
51  // Windows version:
52  HANDLE hDir = CreateFileA(
53  path.c_str(),
54  FILE_LIST_DIRECTORY,
55  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
56  NULL, //security attributes
57  OPEN_EXISTING,
58  FILE_FLAG_BACKUP_SEMANTICS,
59  NULL);
60  if( hDir == INVALID_HANDLE_VALUE )
61  {
62  m_hNotif=NULL;
63  THROW_EXCEPTION("FindFirstChangeNotificationA returned error!");
64  }
65 
66  m_hNotif = static_cast<void*>(hDir);
67 
68  m_watchThread = mrpt::system::createThreadFromObjectMethod(this, &CFileSystemWatcher::thread_win32_watch);
69 
70 #else
71 # if MRPT_HAS_INOTIFY
72  // Linux version:
73  m_wd = -1;
74 
75  m_fd = inotify_init();
76  if (m_fd < 0)
77  THROW_EXCEPTION("inotify_init returned error!");
78 
79  // Create watcher:
80  m_wd = inotify_add_watch(
81  m_fd,
82  path.c_str(),
83  IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM | IN_CREATE | IN_ACCESS
84  );
85 
86  if (m_wd < 0)
87  THROW_EXCEPTION("inotify_add_watch returned error!");
88 # endif
89 #endif
90  MRPT_END
91 }
92 
93 
94 /*---------------------------------------------------------------
95  Destructor
96  ---------------------------------------------------------------*/
98 {
99 #ifdef MRPT_OS_WINDOWS
100  // Windows version:
101  if (m_hNotif)
102  {
103  // Kill thread:
104  if (!m_watchThread.isClear())
105  mrpt::system::terminateThread(m_watchThread);
106  CloseHandle(HANDLE(m_hNotif));
107  m_hNotif=NULL;
108  }
109 #else
110 # if MRPT_HAS_INOTIFY
111  // Linux version:
112  if (m_fd >= 0)
113  {
114  close(m_fd);
115  m_fd = -1;
116  if (m_wd>=0)
117  inotify_rm_watch(m_fd, m_wd);
118  }
119 # endif
120 #endif
121 }
122 
123 /*---------------------------------------------------------------
124  getChanges
125  ---------------------------------------------------------------*/
127 {
128  out_list.clear();
129 
130 #ifdef MRPT_OS_WINDOWS
131  // Windows version:
132  ASSERTMSG_(m_hNotif!=NULL,"CFileSystemWatcher was not initialized correctly.")
133 
134  // Work is done in thread_win32_watch().
135  // Just check for incoming mail:
136  while (!m_queue_events_win32.empty())
137  {
138  TFileSystemChange *obj = m_queue_events_win32.get();
139  if (obj)
140  {
141  out_list.push_back(*obj);
142  delete obj;
143  }
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, NULL, NULL, &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(&event_val, &buf[i], sizeof(event_val)); // Was: event = (struct inotify_event *) ;
212  struct inotify_event *event = &event_val;
213 
214  i += EVENT_SIZE + event->len;
215 
216 // printf ("wd=%d mask=%u cookie=%u len=%u\n",event->wd, event->mask,event->cookie, event->len);
217 
218  string eventName;
219  if (event->len) eventName = event->name;
220 
221  // Add event to output list:
222  // ---------------------------------------------
223  if ( 0==(event->mask & IN_UNMOUNT) &&
224  0==(event->mask & IN_Q_OVERFLOW) &&
225  0==(event->mask & IN_IGNORED) )
226  {
227  TFileSystemChange newEntry;
228 
229  newEntry.path = m_watchedDirectory + eventName;
230  newEntry.isDir = event->mask & IN_ISDIR;
231  newEntry.eventModified = event->mask & IN_MODIFY;
232  newEntry.eventCloseWrite = event->mask & IN_CLOSE_WRITE;
233  newEntry.eventDeleted = event->mask & IN_DELETE;
234  newEntry.eventMovedTo = event->mask & IN_MOVED_TO;
235  newEntry.eventMovedFrom = event->mask & IN_MOVED_FROM;
236  newEntry.eventCreated = event->mask & IN_CREATE;
237  newEntry.eventAccessed = event->mask & IN_ACCESS;
238 
239  out_list.push_back( newEntry );
240  }
241  }
242 
243  }
244 # endif
245 #endif
246 
247 }
248 
249 
250 #ifdef MRPT_OS_WINDOWS
251 
252 void CFileSystemWatcher::thread_win32_watch()
253 {
254 
255  uint8_t buf[8*1024];
256  DWORD dwRead=0;
257 
258  while(ReadDirectoryChangesW(
259  HANDLE(m_hNotif),
260  buf,
261  sizeof(buf),
262  false, // No subtree
263  FILE_NOTIFY_CHANGE_FILE_NAME |
264  FILE_NOTIFY_CHANGE_DIR_NAME |
265  FILE_NOTIFY_CHANGE_ATTRIBUTES |
266  FILE_NOTIFY_CHANGE_SIZE |
267  FILE_NOTIFY_CHANGE_LAST_WRITE |
268  FILE_NOTIFY_CHANGE_LAST_ACCESS |
269  FILE_NOTIFY_CHANGE_CREATION,
270  &dwRead,
271  NULL,
272  NULL))
273  {
274  // Interpret read data as FILE_NOTIFY_INFORMATION:
275  // There might be several notifications in the same data block:
276  size_t idx=0;
277  for(;;)
278  {
279  // Yep... this is MS's idea of a beautiful and easy way to return data:
280  FILE_NOTIFY_INFORMATION *fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buf[idx]);
281 
282  // Extract the name (stored as a WCHAR*) into a UTF-8 std::string:
283  ASSERTMSG_(fni->FileNameLength<10000,"Name length >10K... this is probably an error")
284 
285  int reqLen = WideCharToMultiByte(CP_UTF8,0,fni->FileName,fni->FileNameLength >> 1,NULL,0,NULL, NULL);
286  std::vector<char> tmpBuf(reqLen);
287  int actLen = WideCharToMultiByte(CP_UTF8,0,fni->FileName,fni->FileNameLength >> 1,&tmpBuf[0],tmpBuf.size(),NULL, NULL);
288  ASSERTMSG_(actLen>0,"Error converting filename from WCHAR* to UTF8")
289 
290  const std::string filName(&tmpBuf[0],actLen);
291 
292  TFileSystemChange newEntry;
293  newEntry.path = m_watchedDirectory + filName;
294  newEntry.isDir = mrpt::system::directoryExists(newEntry.path);
295 
296  // Fill out the new entry:
297  switch (fni->Action)
298  {
299  case FILE_ACTION_ADDED:
300  {
301  newEntry.eventCreated = true;
302  m_queue_events_win32.push(new TFileSystemChange(newEntry));
303  } break;
304  case FILE_ACTION_REMOVED:
305  {
306  newEntry.eventDeleted = true;
307  m_queue_events_win32.push(new TFileSystemChange(newEntry));
308  } break;
309  case FILE_ACTION_MODIFIED:
310  {
311  newEntry.eventModified = true;
312  m_queue_events_win32.push(new TFileSystemChange(newEntry));
313  } break;
314  case FILE_ACTION_RENAMED_OLD_NAME:
315  {
316  newEntry.eventMovedFrom = true;
317  m_queue_events_win32.push(new TFileSystemChange(newEntry));
318  } break;
319  case FILE_ACTION_RENAMED_NEW_NAME:
320  {
321  newEntry.eventMovedTo = true;
322  m_queue_events_win32.push(new TFileSystemChange(newEntry));
323  } break;
324  }
325 
326  // Next entry?
327  if (fni->NextEntryOffset>0)
328  idx+=fni->NextEntryOffset;
329  else break; // done
330  }
331  }
332 
333  printf("Done!");
334 }
335 #endif
void BASE_IMPEXP memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) MRPT_NO_THROWS
An OS and compiler independent version of "memcpy".
Definition: os.cpp:358
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:29
#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:4349
GLsizei GLsizei GLuint * obj
Definition: glext.h:3902
std::string m_watchedDirectory
Ended in "/".
unsigned char uint8_t
Definition: rptypes.h:43
#define MRPT_END
std::deque< TFileSystemChange > TFileSystemChangeList
GLsizei const GLchar ** string
Definition: glext.h:3919
TThreadHandle createThreadFromObjectMethod(CLASS *obj, void(CLASS::*func)(PARAM), PARAM param)
Creates a new thread running a non-static method (so it will have access to "this") from another meth...
Definition: threads.h:121
#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)
void BASE_IMPEXP terminateThread(TThreadHandle &threadHandle) MRPT_NO_THROWS
Terminate a thread, giving it no choice to delete objects, etc (use only as a last resource) ...
Definition: threads.cpp:379
Each of the changes detected by utils::CFileSystemWatcher.
GLsizeiptr size
Definition: glext.h:3779
bool BASE_IMPEXP 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:132
#define ASSERTMSG_(f, __ERROR_MSG)
CFileSystemWatcher(const std::string &path)
Creates the subscription to a specified path.



Page generated by Doxygen 1.8.14 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at lun oct 28 01:39:17 CET 2019