Leosac  0.8.0
Open Source Access Control
unixfilewatcher.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014-2016 Leosac
3 
4  This file is part of Leosac.
5 
6  Leosac is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Affero General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  Leosac is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Affero General Public License for more details.
15 
16  You should have received a copy of the GNU Affero General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
26 #include "unixfilewatcher.hpp"
27 
28 extern "C" {
29 #include <sys/inotify.h>
30 #include <unistd.h>
31 }
32 
34 #include "tools/log.hpp"
35 #include "tools/unixsyscall.hpp"
36 
37 using namespace Leosac::Tools;
38 
40  : _isRunning(false)
41 {
42  if ((_inotifyFd = inotify_init()) == -1)
43  throw(FsException(UnixSyscall::getErrorString("inotify_init", errno)));
44 }
45 
47 {
48  try
49  {
50  if (close(_inotifyFd) == -1)
51  throw(FsException(UnixSyscall::getErrorString("close", errno)));
52  }
53  catch (const FsException &e)
54  {
55  ERROR("Exception caught: " << e.what());
56  }
57 }
58 
60 {
61  _isRunning = true;
62  INFO("inotify start");
63  _thread = std::thread([this]() { run(); });
64 }
65 
67 {
68  _isRunning = false;
69  INFO("inotify stop");
70  _thread.join();
71  for (auto watch : _watches)
72  {
73  if (inotify_rm_watch(_inotifyFd, watch.first) == -1)
74  throw(
75  FsException(UnixSyscall::getErrorString("inotify_rm_watch", errno)));
76  }
77  _watches.clear();
78 }
79 
80 void UnixFileWatcher::watchFile(const std::string &path)
81 {
82  std::uint32_t mask = IN_CLOSE_WRITE;
83  WatchParams params;
84  UnixFd watch;
85 
86  if ((watch = inotify_add_watch(_inotifyFd, path.c_str(), mask)) == -1)
87  throw(FsException(UnixSyscall::getErrorString("inotify_add_watch", errno)));
88  params.path = path;
89  params.mask = 0;
90  _watches[watch] = params;
91 }
92 
93 bool UnixFileWatcher::fileHasChanged(const std::string &path) const
94 {
95  for (auto &param : _watches)
96  {
97  if (param.second.path == path)
98  return ((param.second.mask & IN_CLOSE_WRITE) > 0);
99  }
100  throw(FsException("no registered watch for path:" + path));
101 }
102 
103 void UnixFileWatcher::fileReset(const std::string &path)
104 {
105  for (auto &param : _watches)
106  {
107  if (param.second.path == path)
108  {
109  param.second.mask = 0;
110  return;
111  }
112  }
113  throw(FsException("no registered watch for path:" + path));
114 }
115 
116 std::size_t UnixFileWatcher::size() const
117 {
118  return (_watches.size());
119 }
120 
122 {
123  struct timeval timeoutStruct;
124  int ret;
125  long timeout = DefaultTimeoutMs;
126  fd_set readSet;
127  std::size_t buflen = 1024 * (sizeof(struct inotify_event) + 16);
128  char buf[buflen];
129  int len;
130  inotify_event *event;
131 
132  while (_isRunning)
133  {
134  FD_ZERO(&readSet);
135  FD_SET(_inotifyFd, &readSet);
136  timeoutStruct.tv_sec = timeout / 1000;
137  timeoutStruct.tv_usec = (timeout % 1000) * 1000;
138  if ((ret = ::select(_inotifyFd + 1, &readSet, nullptr, nullptr,
139  &timeoutStruct)) == -1)
140  {
141  if (errno != EINTR)
142  throw(FsException(UnixSyscall::getErrorString("select", errno)));
143  else
144  ERROR(UnixSyscall::getErrorString("select", errno));
145  }
146  else if (ret > 0)
147  {
148  if (!FD_ISSET(_inotifyFd, &readSet))
149  throw(FsException("unexpected file descriptor set"));
150  if ((len = read(_inotifyFd, buf, buflen)) == -1)
151  {
152  if (errno != EINTR)
153  throw(FsException(UnixSyscall::getErrorString("read", errno)));
154  }
155  else if (!len)
156  throw(FsException("nothing was read"));
157  else
158  {
159  for (int i = 0; i < len;)
160  {
161  event = reinterpret_cast<inotify_event *>(
162  &buf[i]); // NOTE Alignment should not be an issue here
163  _watches.at(event->wd).mask |= event->mask;
164  i += sizeof(inotify_event) + event->len;
165  }
166  }
167  }
168  }
169 }
LEOSACException::what
virtual const char * what() const noexcept final
Definition: leosacexception.hpp:53
Leosac::Tools::UnixFileWatcher::run
void run()
Definition: unixfilewatcher.cpp:121
Leosac::Tools::UnixFileWatcher::watchFile
void watchFile(const std::string &path)
Definition: unixfilewatcher.cpp:80
Leosac::Tools::UnixFileWatcher::UnixFileWatcher
UnixFileWatcher()
Definition: unixfilewatcher.cpp:39
unixsyscall.hpp
unix syscall helper functions
Leosac::Tools::UnixFileWatcher::start
void start()
Definition: unixfilewatcher.cpp:59
Leosac::Tools::UnixFileWatcher::_inotifyFd
UnixFd _inotifyFd
Definition: unixfilewatcher.hpp:80
Leosac::Tools::UnixFileWatcher::_isRunning
std::atomic< bool > _isRunning
Definition: unixfilewatcher.hpp:79
ERROR
@ ERROR
Definition: log.hpp:32
Leosac::Tools::UnixFileWatcher::stop
void stop()
Definition: unixfilewatcher.cpp:66
Leosac::Tools::UnixFileWatcher::_thread
std::thread _thread
Definition: unixfilewatcher.hpp:78
INFO
@ INFO
Definition: log.hpp:34
Leosac::Tools::UnixFileWatcher::WatchParams::path
std::string path
Definition: unixfilewatcher.hpp:44
Leosac::Tools::UnixSyscall::getErrorString
static std::string getErrorString(const std::string &func, int errNo)
Definition: unixsyscall.cpp:32
Leosac::Tools::UnixFileWatcher::size
std::size_t size() const
Definition: unixfilewatcher.cpp:116
Leosac::Tools::UnixFileWatcher::_watches
Watches _watches
Definition: unixfilewatcher.hpp:81
FsException
Definition: fsexception.hpp:33
Leosac::Tools::UnixFileWatcher::fileReset
void fileReset(const std::string &path)
Definition: unixfilewatcher.cpp:103
Leosac::Tools::UnixFileWatcher::WatchParams
Definition: unixfilewatcher.hpp:42
Leosac::Tools::UnixFileWatcher::UnixFd
int UnixFd
Definition: unixfilewatcher.hpp:41
fsexception.hpp
Exception class for filesystem related errors.
log.hpp
Leosac::Tools::UnixFileWatcher::~UnixFileWatcher
~UnixFileWatcher()
Definition: unixfilewatcher.cpp:46
Leosac::Tools::UnixFileWatcher::fileHasChanged
bool fileHasChanged(const std::string &path) const
Definition: unixfilewatcher.cpp:93
Leosac::Tools::UnixFileWatcher::WatchParams::mask
int mask
Definition: unixfilewatcher.hpp:45
Leosac::Tools
Definition: DatabaseLogSink.hpp:27
Leosac::Tools::UnixFileWatcher::DefaultTimeoutMs
static const long DefaultTimeoutMs
Definition: unixfilewatcher.hpp:49
unixfilewatcher.hpp
UnixFileWatcher class declaration.