# Checks for libraries.
# Checks for header files.
+AC_CHECK_HEADERS([sys/inotify.h])
# Checks for typedefs, structures, and compiler characteristics.
+AC_CHECK_SIZEOF([time_t])
+AC_CHECK_SIZEOF([int])
+AC_CHECK_SIZEOF([long])
+AC_CHECK_SIZEOF([long long])
# Checks for library functions.
AC_CHECK_FUNCS([strerror])
--- /dev/null
+//
+// Copyright © 2011 Richard Kettlewell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+#include <config.h>
+
+#if HAVE_SYS_INOTIFY_H
+
+#include "Watcher.h"
+#include "InotifyWatcher.h"
+#include <sys/inotify.h>
+#include <cerrno>
+#include <cstdio>
+#include "log.h"
+
+InotifyWatcher::InotifyWatcher(const std::string &path_arg,
+ Watcher *watcher):
+ WatcherImplementation(path_arg, watcher),
+ base(getBaseName(path)),
+ dir(getDirName(path)),
+ ifd(-1),
+ file_wd(-1),
+ dir_wd(-1) {
+ // Get an inotify FD
+ if((ifd = inotify_init()) < 0)
+ throw SystemError("inotify_init", errno);
+ // Watch the containing directory
+ if((dir_wd = inotify_add_watch(ifd, dir.c_str(),
+ IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO)) < 0)
+ throw SystemError("inotify_add_watch for " + dir, errno);
+ // Try to open the file. This will add file_wd if it exists.
+ openFile();
+ // Discard initial content
+ if(fp) {
+ if(fseek(fp, 0, SEEK_END) < 0)
+ throw IOError("seeking " + path, errno);
+ }
+}
+
+InotifyWatcher::~InotifyWatcher() {
+ if(ifd >= 0)
+ close(ifd);
+}
+
+int InotifyWatcher::pollfd(time_t &) const {
+ return ifd;
+}
+
+void InotifyWatcher::openFile() {
+ if(!fp) {
+ // Try to open the file. If it doesn't exist, that's OK, we'll continue to
+ // wait for it to come into existence.
+ if(!(fp = fopen(path.c_str(), "r"))) {
+ if(errno != ENOENT)
+ throw IOError("opening " + path, errno);
+ } else {
+ // If we did open it then detect changes to it.
+ // (But see below concerning IN_*_SELF.)
+ if((file_wd = inotify_add_watch(ifd, path.c_str(),
+ IN_MODIFY
+ |IN_MOVE_SELF
+ |IN_DELETE_SELF)) < 0)
+ throw SystemError("inotify_add_watch for " + path, errno);
+ }
+ }
+}
+
+void InotifyWatcher::closeFile() {
+ if(fp) {
+ if(line.size()) {
+ processLine(line);
+ line.clear();
+ }
+ if(file_wd >= 0) {
+ if(inotify_rm_watch(ifd, file_wd) < 0) {
+ file_wd = -1;
+ // On 2.6.26 we get a spurious EINVAL removing a wd sometimes. It
+ // doesn't happen on 2.6.32. We ignore it and issue a warning instead.
+ // There's not much else that can be done.
+ if(errno == EINVAL)
+ warn("inotify_rm_watch: %s", strerror(errno));
+ else
+ throw SystemError("inotify_rm_watch for " + path, errno);
+ }
+ file_wd = -1;
+ }
+ fclose(fp);
+ fp = NULL;
+ }
+}
+
+void InotifyWatcher::work() {
+ char buffer[4096];
+ int n = read(ifd, buffer, sizeof buffer);
+ if(n < 0) {
+ if(errno != EINTR && errno != EAGAIN)
+ throw SystemError("read from inotify fd", errno);
+ return;
+ }
+ // We might have more than one event.
+ char *ptr = buffer;
+ while(n > 0) {
+ // Make sure we have a whole event
+ if((size_t)n < sizeof (struct inotify_event))
+ throw SystemError("short read from inotify fd");
+ const struct inotify_event &event = *(const struct inotify_event *)ptr;
+ const size_t total = sizeof event + event.len;
+ if((size_t)n < total)
+ throw SystemError("short read from inotify fd");
+
+ // See if the file contents changed.
+ if(event.wd == file_wd) {
+ // Read as much as we can from the file, whatever happened
+ readLines();
+ // If the file was deleted or renamed away, we're done with this version
+ // of it. Note that we don't seem to get (at least) IN_DELETE_SELF
+ // reliably! However we handle the cases that ought to produce it below
+ // in the dir_wd checking too.
+ if(event.mask & (IN_MOVE_SELF|IN_DELETE_SELF)) {
+ // Close the file.
+ closeFile();
+ }
+ }
+
+ if(event.wd == dir_wd
+ && event.len
+ && !strcmp(event.name, base.c_str())) {
+ // The file has been newly created or replaced by another file. Close
+ // the old version (if open).
+ closeFile();
+ // Open the new version (if it exists).
+ openFile();
+ if(fp) {
+ // Something might have been written to it since it was created but
+ // before we started watching it, so we check straight away.
+ readLines();
+ }
+ }
+ ptr += total;
+ n -= total;
+ }
+}
+
+#endif
+
+/*
+Local Variables:
+mode:c++
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
--- /dev/null
+//
+// Copyright © 2011 Richard Kettlewell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+#ifndef INOTIFYWATCHER_H
+#define INOTIFYWATCHER_H
+
+#if HAVE_SYS_INOTIFY_H
+#include "Watcher.h"
+
+class InotifyWatcher: public WatcherImplementation {
+public:
+ InotifyWatcher(const std::string &path,
+ Watcher *watcher);
+ ~InotifyWatcher();
+ int pollfd(time_t &limit) const;
+ void work();
+private:
+ const std::string base; // base filename
+ const std::string dir; // containing directory
+ int ifd; // inotify descriptor
+ int file_wd; // file watch descriptor or -1
+ int dir_wd; // directory watch descriptor
+
+ void openFile(); // try to ensure the watched file open
+ void closeFile(); // close the watched file if open
+
+};
+#endif
+
+#endif /* INOTIFYWATCHER_H */
+
+/*
+Local Variables:
+mode:c++
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
libutils_a_SOURCES=Address.cc ConfFile.cc Regex.cc StdioFile.cc \
Watcher.cc Address.h ConfFile.h Regex.h StdioFile.h Watcher.h log.h \
log.cc IOError.cc IOError.h nonblock.cc utils.h execute.cc \
-BlockMethod.h BlockMethod.cc
+BlockMethod.h BlockMethod.cc WatcherImplementation.h \
+WatcherImplementation.cc InotifyWatcher.cc InotifyWatcher.h \
+PollingWatcher.cc PollingWatcher.h timeval.h
blockad_SOURCES=blockad.cc iptables.cc hosts.deny.cc
LDADD=libutils.a
EXTRA_DIST=${man_MANS}
--- /dev/null
+//
+// Copyright © 2011 Richard Kettlewell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+#include <config.h>
+#include "Watcher.h"
+#include "PollingWatcher.h"
+#include "IOError.h"
+#include <cerrno>
+#include <sys/stat.h>
+
+PollingWatcher::PollingWatcher(const std::string &path,
+ Watcher *watcher):
+ WatcherImplementation(path, watcher),
+ next(time(NULL)),
+ dev(-1),
+ ino(-1) {
+ // Try opening the file
+ if((fp = fopen(path.c_str(), "r"))) {
+ if(fseek(fp, 0, SEEK_END) < 0)
+ throw IOError("seeking " + path, errno);
+ }
+}
+
+PollingWatcher::~PollingWatcher() {
+ if(fp)
+ fclose(fp);
+}
+
+int PollingWatcher::pollfd(time_t &limit) const {
+ if(limit > next)
+ limit = next;
+ return -1;
+}
+
+void PollingWatcher::openFile() {
+ struct stat sb;
+ if(!fp) {
+ if(!(fp = fopen(path.c_str(), "r"))) {
+ if(errno != ENOENT)
+ throw IOError("opening " + path, errno);
+ return;
+ }
+ if(fstat(fileno(fp), &sb) < 0)
+ throw IOError("fstat " + path, errno);
+ dev = sb.st_dev;
+ ino = sb.st_ino;
+ }
+}
+
+void PollingWatcher::closeFile() {
+ if(fp) {
+ if(line.size()) {
+ processLine(line);
+ line.clear();
+ }
+ fclose(fp);
+ fp = NULL;
+ dev = -1;
+ ino = -1;
+ }
+}
+
+void PollingWatcher::work() {
+ // Check again in a second
+ next = time(NULL) + 1;
+ // If the file is open, verify that we're still reading the right one
+ if(fp) {
+ struct stat sb;
+ if(stat(path.c_str(), &sb) < 0
+ || sb.st_ino != ino
+ || sb.st_dev != dev)
+ closeFile();
+ }
+ // If the file wasn't open, see if it is now
+ if(!fp) {
+ openFile();
+ if(!fp)
+ return;
+ }
+ // The file must now be open
+ readLines();
+}
+
+/*
+Local Variables:
+mode:c++
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
--- /dev/null
+//
+// Copyright © 2011 Richard Kettlewell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+#ifndef POLLINGWATCHER_H
+#define POLLINGWATCHER_H
+
+#include "Watcher.h"
+
+class PollingWatcher: public WatcherImplementation {
+public:
+ PollingWatcher(const std::string &path,
+ Watcher *watcher);
+ ~PollingWatcher();
+ int pollfd(time_t &limit) const;
+ void work();
+private:
+ time_t next;
+ dev_t dev;
+ ino_t ino;
+ void openFile();
+ void closeFile();
+};
+
+#endif /* POLLINGWATCHER_H */
+
+/*
+Local Variables:
+mode:c++
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
//
#include <config.h>
#include "Watcher.h"
-#include <sys/inotify.h>
-#include <cerrno>
-#include <cstdio>
-#include "log.h"
+#include "InotifyWatcher.h"
+#include "PollingWatcher.h"
-Watcher::Watcher(const std::string &path_arg): path(path_arg),
- base(getBaseName(path)),
- dir(getDirName(path)),
- fp(NULL),
- ifd(-1),
- file_wd(-1),
- dir_wd(-1) {
- // Get an inotify FD
- if((ifd = inotify_init()) < 0)
- throw SystemError("inotify_init", errno);
- // Watch the containing directory
- if((dir_wd = inotify_add_watch(ifd, dir.c_str(),
- IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO)) < 0)
- throw SystemError("inotify_add_watch for " + dir, errno);
- // Try to open the file. This will add file_wd if it exists.
- openFile();
- // Discard initial content
- if(fp) {
- if(fseek(fp, 0, SEEK_END) < 0)
- throw IOError("seeking " + path, errno);
- }
+Watcher::Watcher(const std::string &path): impl(new WATCHER(path, this)) {
}
Watcher::~Watcher() {
- if(ifd >= 0)
- close(ifd);
- if(fp)
- fclose(fp);
-}
-
-void Watcher::openFile() {
- if(!fp) {
- // Try to open the file. If it doesn't exist, that's OK, we'll continue to
- // wait for it to come into existence.
- if(!(fp = fopen(path.c_str(), "r"))) {
- if(errno != ENOENT)
- throw IOError("opening " + path, errno);
- } else {
- // If we did open it then detect changes to it.
- // (But see below concerning IN_*_SELF.)
- if((file_wd = inotify_add_watch(ifd, path.c_str(),
- IN_MODIFY
- |IN_MOVE_SELF
- |IN_DELETE_SELF)) < 0)
- throw SystemError("inotify_add_watch for " + path, errno);
- }
- }
-}
-
-void Watcher::closeFile() {
- if(fp) {
- if(line.size()) {
- processLine(line);
- line.clear();
- }
- if(file_wd >= 0) {
- if(inotify_rm_watch(ifd, file_wd) < 0) {
- file_wd = -1;
- // On 2.6.26 we get a spurious EINVAL removing a wd sometimes. It
- // doesn't happen on 2.6.32. We ignore it and issue a warning instead.
- // There's not much else that can be done.
- if(errno == EINVAL)
- warn("inotify_rm_watch: %s", strerror(errno));
- else
- throw SystemError("inotify_rm_watch for " + path, errno);
- }
- file_wd = -1;
- }
- fclose(fp);
- fp = NULL;
- }
-}
-
-std::string Watcher::getDirName(const std::string &p) {
- std::string::size_type s = p.rfind('/');
- if(s == std::string::npos)
- return ".";
- else
- return std::string(p, 0, s + 1);
-}
-
-std::string Watcher::getBaseName(const std::string &p) {
- std::string::size_type s = p.rfind('/');
- if(s == std::string::npos)
- return p;
- else
- return std::string(p, s + 1);
-}
-
-void Watcher::work() {
- char buffer[4096];
- int n = read(ifd, buffer, sizeof buffer);
- if(n < 0) {
- if(errno != EINTR && errno != EAGAIN)
- throw SystemError("read from inotify fd", errno);
- return;
- }
- // We might have more than one event.
- char *ptr = buffer;
- while(n > 0) {
- // Make sure we have a whole event
- if((size_t)n < sizeof (struct inotify_event))
- throw SystemError("short read from inotify fd");
- const struct inotify_event &event = *(const struct inotify_event *)ptr;
- const size_t total = sizeof event + event.len;
- if((size_t)n < total)
- throw SystemError("short read from inotify fd");
-
- // See if the file contents changed.
- if(event.wd == file_wd) {
- // Read as much as we can from the file, whatever happened
- readLines();
- // If the file was deleted or renamed away, we're done with this version
- // of it. Note that we don't seem to get (at least) IN_DELETE_SELF
- // reliably! However we handle the cases that ought to produce it below
- // in the dir_wd checking too.
- if(event.mask & (IN_MOVE_SELF|IN_DELETE_SELF)) {
- // Close the file.
- closeFile();
- }
- }
-
- if(event.wd == dir_wd
- && event.len
- && !strcmp(event.name, base.c_str())) {
- // The file has been newly created or replaced by another file. Close
- // the old version (if open).
- closeFile();
- // Open the new version (if it exists).
- openFile();
- if(fp) {
- // Something might have been written to it since it was created but
- // before we started watching it, so we check straight away.
- readLines();
- }
- }
- ptr += total;
- n -= total;
- }
-}
-
-void Watcher::readLines() {
- int c;
- for(;;) {
- // Read the line up to the next newline or EOF
- while((c = getc(fp)) != EOF) {
- line += c;
- if(c == '\n')
- break;
- }
- if(c == '\n') {
- // We got a complete line
- processLine(line);
- line.clear();
- } else {
- // We did not get a complete line. If it was an error, close it
- // and throw.
- if(ferror(fp)) {
- closeFile();
- throw IOError("reading " + path, errno);
- }
- // Otherwise it must be EOF.
- // Make sure future reads, after the file is extended, will succeed.
- clearerr(fp);
- // Done for now. If the file is deleted before a newline is read,
- // closeFile() will handle the partial line.
- return;
- }
- }
+ delete(impl);
}
/*
#include <stdexcept>
#include <cstring>
+#if HAVE_SYS_INOTIFY_H
+# define WATCHER InotifyWatcher
+#else
+# define WATCHER PollingWatcher
+#endif
+
+class Watcher;
+
+// Base class for watcher implementations
+class WatcherImplementation {
+public:
+ WatcherImplementation(const std::string &path_,
+ Watcher *watcher_): path(path_),
+ fp(NULL),
+ watcher(watcher_) {
+ }
+ virtual ~WatcherImplementation();
+ virtual int pollfd(time_t &limit) const = 0;
+ virtual void work() = 0;
+ inline void processLine(const std::string &line);
+protected:
+ const std::string path;
+ FILE *fp;
+ std::string line; // line read so far
+
+ virtual void closeFile() = 0;
+
+ static std::string getBaseName(const std::string &); // get base name
+ static std::string getDirName(const std::string &); // get directory name
+
+ void readLines(); // read lines from the watch file
+private:
+ Watcher *watcher;
+};
+
// Watch a filename and supply lines written to it to a callback.
class Watcher {
public:
virtual ~Watcher();
// Return the file descriptor to poll
- inline int pollfd() const { return ifd; }
+ int pollfd(time_t &limit) const { return impl->pollfd(limit); }
// Do some work. Call this when pollfd() polls readable, or just
// repeatedly if you didn't make it nonblocking.
- void work();
+ void work() { impl->work(); }
// Passed a new line from the watched file. line includes the trailing '\n',
// if there was one; there may not be if a file is finished off without a
};
private:
- const std::string path; // filename to watch
- const std::string base; // base filename
- const std::string dir; // containing directory
- std::string line; // line read so far
- FILE *fp; // (may be NULL) current file
- int ifd; // inotify descriptor
- int file_wd; // file watch descriptor or -1
- int dir_wd; // directory watch descriptor
-
- void openFile(); // try to ensure the watched file open
- void closeFile(); // close the watched file if open
- void readLines(); // read lines from the watch file
- static std::string getBaseName(const std::string &); // get base name
- static std::string getDirName(const std::string &); // get directory name
+ WatcherImplementation *impl;
};
+inline void WatcherImplementation::processLine(const std::string &line) {
+ watcher->processLine(line);
+}
+
#endif /* WATCHER_H */
/*
--- /dev/null
+//
+// Copyright © 2011 Richard Kettlewell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+#include <config.h>
+#include "Watcher.h"
+#include "IOError.h"
+#include <cerrno>
+
+WatcherImplementation::~WatcherImplementation() {
+ if(fp)
+ fclose(fp);
+}
+
+std::string WatcherImplementation::getDirName(const std::string &p) {
+ std::string::size_type s = p.rfind('/');
+ if(s == std::string::npos)
+ return ".";
+ else
+ return std::string(p, 0, s + 1);
+}
+
+std::string WatcherImplementation::getBaseName(const std::string &p) {
+ std::string::size_type s = p.rfind('/');
+ if(s == std::string::npos)
+ return p;
+ else
+ return std::string(p, s + 1);
+}
+
+void WatcherImplementation::readLines() {
+ int c;
+ for(;;) {
+ // Read the line up to the next newline or EOF
+ while((c = getc(fp)) != EOF) {
+ line += c;
+ if(c == '\n')
+ break;
+ }
+ if(c == '\n') {
+ // We got a complete line
+ processLine(line);
+ line.clear();
+ } else {
+ // We did not get a complete line. If it was an error, close it
+ // and throw.
+ if(ferror(fp)) {
+ closeFile();
+ throw IOError("reading " + path, errno);
+ }
+ // Otherwise it must be EOF.
+ // Make sure future reads, after the file is extended, will succeed.
+ clearerr(fp);
+ // Done for now. If the file is deleted before a newline is read,
+ // closeFile() will handle the partial line.
+ return;
+ }
+ }
+}
+
+/*
+Local Variables:
+mode:c++
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
#include "Address.h"
#include "Regex.h"
#include "BlockMethod.h"
+#include "timeval.h"
#include "log.h"
#include "utils.h"
#include <sys/select.h>
std::vector<Watcher *> &oldWatchers) {
size_t i, j;
std::vector<Watcher *> newWatchers;
+ time_t limit = 0;
+ int fd;
for(i = 0; i < newConfig->files.size(); ++i) {
// Try and re-use the existing watcher
if(oldConfig) {
}
// We didn't manage to re-use an existing watcher
newWatchers.push_back(new BanWatcher(newConfig->files[i]));
- nonblock(newWatchers[i]->pollfd());
+ // Make the new watcher's FD nonblocking (if it has one)
+ fd = newWatchers[i]->pollfd(limit);
+ if(fd >= 0)
+ nonblock(fd);
}
// Delete redundant watchers
for(j = 0; j < oldWatchers.size(); ++j)
// Construct FD set
int maxfd;
fd_set fds;
+ struct timeval now, delta;
+ time_t limit = TIME_MAX;
FD_ZERO(&fds);
FD_SET(signal_pipe[0], &fds);
maxfd = signal_pipe[0];
for(size_t i = 0; i < watchers.size(); ++i) {
- const int fd = watchers[i]->pollfd();
- FD_SET(fd, &fds);
- if(fd > maxfd)
- maxfd = fd;
+ const int fd = watchers[i]->pollfd(limit);
+ if(fd >= 0) {
+ FD_SET(fd, &fds);
+ if(fd > maxfd)
+ maxfd = fd;
+ }
}
// Unblock signal while waiting
if(sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL) < 0) {
error("sigprocmask: %s", strerror(errno));
exit(-1);
}
- n = select(maxfd + 1, &fds, NULL, NULL, NULL);
+ if(limit != TIME_MAX) {
+ gettimeofday(&now, NULL);
+ delta = limit - now;
+ if(delta < 0) {
+ delta.tv_sec = 0;
+ delta.tv_usec = 0;
+ }
+ n = select(maxfd + 1, &fds, NULL, NULL, &delta);
+ } else
+ n = select(maxfd + 1, &fds, NULL, NULL, NULL);
if(sigprocmask(SIG_BLOCK, &sighup_mask, NULL) < 0) {
error("sigprocmask: %s", strerror(errno));
exit(-1);
exit(-1);
}
// Check logfiles for updates
+ gettimeofday(&now, NULL);
for(size_t i = 0; i < watchers.size(); ++i) {
- const int fd = watchers[i]->pollfd();
- if(FD_ISSET(fd, &fds))
+ time_t limit = TIME_MAX;
+ const int fd = watchers[i]->pollfd(limit);
+ if(fd >= 0) {
+ if(FD_ISSET(fd, &fds))
+ watchers[i]->work();
+ } else if(now >= limit)
watchers[i]->work();
}
// Check for signals
--- /dev/null
+//
+// Copyright © 2011 Richard Kettlewell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+#ifndef TIMEVAL_H
+#define TIMEVAL_H
+
+#include <sys/time.h>
+
+#if SIZEOF_TIME_T == SIZEOF_INT
+# define TIME_MAX INT_MAX
+#elif SIZEOF_TIME_T == SIZEOF_LONG
+# define TIME_MAX LONG_MAX
+#elif SIZEOF_TIME_T == SIZEOF_LONG_LONG
+# define TIME_MAX LLONG_MAX
+#else
+# error cannot figure out TIME_MAX
+#endif
+
+inline struct timeval operator-(const struct timeval &a,
+ const struct timeval &b) {
+ struct timeval r;
+ r.tv_sec = a.tv_sec - b.tv_sec;
+ r.tv_usec = a.tv_usec - b.tv_usec;
+ if(r.tv_usec < 0) {
+ r.tv_usec += 1000000;
+ r.tv_sec -= 1;
+ }
+ return r;
+}
+
+inline struct timeval operator-(time_t a,
+ const struct timeval &b) {
+ struct timeval aa;
+ aa.tv_sec = a;
+ aa.tv_usec = 0;
+ return aa - b;
+}
+
+inline bool operator<(const struct timeval &a,
+ const struct timeval &b) {
+ if(a.tv_sec < b.tv_sec)
+ return true;
+ if(a.tv_sec == b.tv_sec && a.tv_usec < b.tv_usec)
+ return true;
+ return false;
+}
+
+inline bool operator<(const struct timeval &a,
+ time_t b) {
+ if(a.tv_sec < b)
+ return true;
+ return false;
+}
+
+inline bool operator>=(const struct timeval &a,
+ time_t b) {
+ if(a.tv_sec >= b)
+ return true;
+ return false;
+}
+
+#endif /* TIMEVAL_H */
+
+/*
+Local Variables:
+mode:c++
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
if(argc != 2)
fprintf(stderr, "Usage: watchfile PATH\n");
MyWatcher w(argv[1]);
- for(;;)
+ for(;;) {
+ time_t limit;
+ if(w.pollfd(limit) < 0)
+ sleep(1);
w.work();
+ }
}