IBR-DTNSuite  0.12
FileMonitor.cpp
Go to the documentation of this file.
1 /*
2  * FileMonitor.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Johannes Morgenroth <morgenroth@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
22 #include "config.h"
23 #include "net/FileMonitor.h"
24 #include "core/BundleCore.h"
25 #include <ibrcommon/Logger.h>
27 
28 #ifdef HAVE_SYS_INOTIFY_H
29 #include <sys/inotify.h>
30 #include <unistd.h>
31 #endif
32 
33 namespace dtn
34 {
35  namespace net
36  {
38  {
39  }
40 
42  {
43  }
44 
45  void inotifysocket::up() throw (ibrcommon::socket_exception)
46  {
47  if (_state != SOCKET_DOWN)
48  throw ibrcommon::socket_exception("socket is already up");
49 
50 #ifdef HAVE_SYS_INOTIFY_H
51  // initialize fd
52  _fd = inotify_init();
53 #endif
54 
55  _state = SOCKET_UP;
56  }
57 
58  void inotifysocket::down() throw (ibrcommon::socket_exception)
59  {
60  if (_state != SOCKET_UP)
61  throw ibrcommon::socket_exception("socket is not up");
62 
63 #ifdef HAVE_SYS_INOTIFY_H
64  for (watch_map::iterator iter = _watch_map.begin(); iter != _watch_map.end(); ++iter)
65  {
66  const int wd = (*iter).first;
67  inotify_rm_watch(this->fd(), wd);
68  }
69  _watch_map.clear();
70 
71  this->close();
72 #endif
73 
75  }
76 
78  {
79 #ifdef HAVE_SYS_INOTIFY_H
80  int wd = inotify_add_watch(this->fd(), path.getPath().c_str(), opts);
81  _watch_map[wd] = path;
82 #endif
83  }
84 
85  ssize_t inotifysocket::read(char *data, size_t len) throw (ibrcommon::socket_exception)
86  {
87 #ifdef HAVE_SYS_INOTIFY_H
88  return ::read(this->fd(), data, len);
89 #else
90  return 0;
91 #endif
92  }
93 
95  : _running(true)
96  {
97  }
98 
100  {
101  join();
102  }
103 
105  {
106  IBRCOMMON_LOGGER_DEBUG_TAG("FileMonitor", 5) << "watch on: " << watch.getPath() << IBRCOMMON_LOGGER_ENDL;
107 
108  if (!watch.isDirectory())
109  throw ibrcommon::Exception("can not watch files, please specify a directory");
110 
111  ibrcommon::socketset socks = _socket.getAll();
112  if (socks.size() == 0) return;
113  inotifysocket &sock = dynamic_cast<inotifysocket&>(**socks.begin());
114 
115 #ifdef HAVE_SYS_INOTIFY_H
116  sock.watch(watch, IN_CREATE | IN_DELETE);
117 #endif
118  _watchset.insert(watch);
119  }
120 
121  void FileMonitor::componentUp() throw ()
122  {
123  // routine checked for throw() on 15.02.2013
124  try {
125  _socket.up();
126  } catch (const ibrcommon::socket_exception &ex) {
127  IBRCOMMON_LOGGER_TAG("FileMonitor", error) << ex.what() << IBRCOMMON_LOGGER_ENDL;
128  }
129  }
130 
132  {
133 #ifdef HAVE_SYS_INOTIFY_H
134  while (_running)
135  {
137 
138  // scan for mounts
139  scan();
140 
141  try {
142  char buf[1024];
143 
144  // select on all bound sockets
145  _socket.select(&fds, NULL, NULL, NULL);
146 
147  // receive from all sockets
148  for (ibrcommon::socketset::iterator iter = fds.begin(); iter != fds.end(); ++iter)
149  {
150  inotifysocket &sock = dynamic_cast<inotifysocket&>(**iter);
151  sock.read((char*)&buf, 1024);
152  }
153 
155  } catch (const ibrcommon::vsocket_interrupt&) {
156  return;
157  } catch (const ibrcommon::vsocket_timeout&) {
158  // scan again after timeout
159  } catch (const ibrcommon::socket_exception&) {
160  return;
161  }
162  }
163 #endif
164  }
165 
167  {
168  _socket.down();
169  }
170 
172  {
173  _socket.down();
174  }
175 
176  void FileMonitor::scan()
177  {
178  IBRCOMMON_LOGGER_DEBUG_TAG("FileMonitor", 5) << "scan for file changes" << IBRCOMMON_LOGGER_ENDL;
179 
180  std::set<ibrcommon::File> watch_set;
181 
182  for (watch_set::iterator iter = _watchset.begin(); iter != _watchset.end(); ++iter)
183  {
184  const ibrcommon::File &path = (*iter);
185  std::list<ibrcommon::File> files;
186 
187  if (path.getFiles(files) == 0)
188  {
189  for (std::list<ibrcommon::File>::iterator iter = files.begin(); iter != files.end(); ++iter)
190  {
191  watch_set.insert(*iter);
192  }
193  }
194  else
195  {
196  IBRCOMMON_LOGGER_TAG("FileMonitor", error) << "scan of " << path.getPath() << " failed" << IBRCOMMON_LOGGER_ENDL;
197  }
198  }
199 
200  // check for new directories
201  for (std::set<ibrcommon::File>::iterator iter = watch_set.begin(); iter != watch_set.end(); ++iter)
202  {
203  const ibrcommon::File &path = (*iter);
204  if (path.isDirectory() && !path.isSystem())
205  {
206  if (!isActive(path)) {
207  adopt(path);
208  }
209  }
210  }
211 
212  // look for gone directories
213  for (std::map<ibrcommon::File, dtn::core::Node>::iterator iter = _active_paths.begin(); iter != _active_paths.end();)
214  {
215  const ibrcommon::File &path = (*iter).first;
216  const dtn::core::Node &node = (*iter).second;
217 
218  if (watch_set.find(path) == watch_set.end())
219  {
221  IBRCOMMON_LOGGER_DEBUG_TAG("FileMonitor", 5) << "Node on drive gone: " << node.getEID().getString() << IBRCOMMON_LOGGER_ENDL;
222  _active_paths.erase(iter++);
223  }
224  else
225  {
226  ++iter;
227  }
228  }
229  }
230 
231  void FileMonitor::adopt(const ibrcommon::File &path)
232  {
233  // look for ".dtndrive" file
234  const ibrcommon::File drivefile = path.get(".dtndrive");
235  if (drivefile.exists())
236  {
237  ibrcommon::ConfigFile cf(drivefile.getPath());
238 
239  try {
240  const dtn::data::EID eid( cf.read<std::string>("EID") );
241 
242  dtn::core::Node n(eid);
243  const std::string uri = "file://" + path.getPath() + "/" + cf.read<std::string>("PATH");
244 
247 
248  _active_paths[path] = n;
249 
250  IBRCOMMON_LOGGER_DEBUG_TAG("FileMonitor", 5) << "Node on drive found: " << n.getEID().getString() << IBRCOMMON_LOGGER_ENDL;
251 
252  } catch (const ibrcommon::ConfigFile::key_not_found&) {};
253  }
254  }
255 
256  bool FileMonitor::isActive(const ibrcommon::File &path) const
257  {
258  return (_active_paths.find(path) != _active_paths.end());
259  }
260 
261  const std::string FileMonitor::getName() const
262  {
263  return "FileMonitor";
264  }
265  }
266 } /* namespace ibrcommon */