impl/linux/file_watch: Support watching directories
This commit is contained in:
parent
3110d21c3c
commit
552c1ec684
@ -12,6 +12,21 @@
|
||||
|
||||
namespace interface {
|
||||
|
||||
bool Filesystem::check_file_extension(const char *path, const char *ext)
|
||||
{
|
||||
return c55fs::checkFileExtension(path, ext);
|
||||
}
|
||||
|
||||
ss_ Filesystem::strip_file_extension(const ss_ &path)
|
||||
{
|
||||
return c55fs::stripFileExtension(path);
|
||||
}
|
||||
|
||||
ss_ Filesystem::strip_file_name(const ss_ &path)
|
||||
{
|
||||
return c55fs::stripFilename(path);
|
||||
}
|
||||
|
||||
struct CFilesystem : public Filesystem
|
||||
{
|
||||
sv_<Node> list_directory(const ss_ &path)
|
||||
|
@ -5,8 +5,12 @@
|
||||
#include <cstring>
|
||||
#include <sys/inotify.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/limits.h> // PATH_MAX
|
||||
#define MODULE "__filewatch"
|
||||
|
||||
#define INOTIFY_BUFSIZE (sizeof(struct inotify_event) + NAME_MAX + 1)
|
||||
#define INOTIFY_STRUCTSIZE (sizeof(struct inotify_event))
|
||||
|
||||
namespace interface {
|
||||
|
||||
struct CFileWatch: FileWatch
|
||||
@ -51,41 +55,50 @@ struct CFileWatch: FileWatch
|
||||
{
|
||||
if(fd != m_fd)
|
||||
return;
|
||||
struct inotify_event in_event;
|
||||
char buf[INOTIFY_BUFSIZE];
|
||||
for(;;){
|
||||
int r = read(fd, &in_event, sizeof(struct inotify_event));
|
||||
if(r != sizeof(struct inotify_event))
|
||||
int r = read(fd, buf, INOTIFY_BUFSIZE);
|
||||
if(r == -1){
|
||||
if(errno == EAGAIN)
|
||||
break;
|
||||
log_w(MODULE, "CFileWatch::report_fd(): read() failed "
|
||||
"on fd=%i: %s", fd, strerror(errno));
|
||||
break;
|
||||
sv_<char> name_buf(in_event.len);
|
||||
read(fd, &name_buf[0], in_event.len);
|
||||
ss_ name(name_buf.begin(), name_buf.end());
|
||||
}
|
||||
if(r < (int)INOTIFY_STRUCTSIZE){
|
||||
throw Exception("CFileWatch::report_fd(): read() -> "+itos(r));
|
||||
}
|
||||
struct inotify_event *in_event = (struct inotify_event*)buf;
|
||||
ss_ name;
|
||||
if(r >= (int)INOTIFY_STRUCTSIZE)
|
||||
name = &buf[INOTIFY_STRUCTSIZE]; // Null-terminated string
|
||||
ss_ mask_s;
|
||||
if(in_event.mask & IN_ACCESS) mask_s += "IN_ACCESS | ";
|
||||
if(in_event.mask & IN_ATTRIB) mask_s += "IN_ATTRIB | ";
|
||||
if(in_event.mask & IN_CLOSE_WRITE) mask_s += "IN_CLOSE_WRITE | ";
|
||||
if(in_event.mask & IN_CLOSE_NOWRITE) mask_s += "IN_CLOSE_NOWRITE | ";
|
||||
if(in_event.mask & IN_CREATE) mask_s += "IN_CREATE | ";
|
||||
if(in_event.mask & IN_DELETE) mask_s += "IN_DELETE | ";
|
||||
if(in_event.mask & IN_DELETE_SELF) mask_s += "IN_DELETE_SELF | ";
|
||||
if(in_event.mask & IN_MODIFY) mask_s += "IN_MODIFY | ";
|
||||
if(in_event.mask & IN_MOVE_SELF) mask_s += "IN_MOVE_SELF | ";
|
||||
if(in_event.mask & IN_MOVED_FROM) mask_s += "IN_MOVED_FROM | ";
|
||||
if(in_event.mask & IN_MOVED_TO) mask_s += "IN_MOVED_TO | ";
|
||||
if(in_event.mask & IN_OPEN) mask_s += "IN_OPEN | ";
|
||||
if(in_event.mask & IN_IGNORED) mask_s += "IN_IGNORED | ";
|
||||
if(in_event.mask & IN_ISDIR) mask_s += "IN_ISDIR | ";
|
||||
if(in_event.mask & IN_Q_OVERFLOW) mask_s += "IN_Q_OVERFLOW | ";
|
||||
if(in_event.mask & IN_UNMOUNT) mask_s += "IN_UNMOUNT | ";
|
||||
if(in_event->mask & IN_ACCESS) mask_s += "IN_ACCESS | ";
|
||||
if(in_event->mask & IN_ATTRIB) mask_s += "IN_ATTRIB | ";
|
||||
if(in_event->mask & IN_CLOSE_WRITE) mask_s += "IN_CLOSE_WRITE | ";
|
||||
if(in_event->mask & IN_CLOSE_NOWRITE) mask_s += "IN_CLOSE_NOWRITE | ";
|
||||
if(in_event->mask & IN_CREATE) mask_s += "IN_CREATE | ";
|
||||
if(in_event->mask & IN_DELETE) mask_s += "IN_DELETE | ";
|
||||
if(in_event->mask & IN_DELETE_SELF) mask_s += "IN_DELETE_SELF | ";
|
||||
if(in_event->mask & IN_MODIFY) mask_s += "IN_MODIFY | ";
|
||||
if(in_event->mask & IN_MOVE_SELF) mask_s += "IN_MOVE_SELF | ";
|
||||
if(in_event->mask & IN_MOVED_FROM) mask_s += "IN_MOVED_FROM | ";
|
||||
if(in_event->mask & IN_MOVED_TO) mask_s += "IN_MOVED_TO | ";
|
||||
if(in_event->mask & IN_OPEN) mask_s += "IN_OPEN | ";
|
||||
if(in_event->mask & IN_IGNORED) mask_s += "IN_IGNORED | ";
|
||||
if(in_event->mask & IN_ISDIR) mask_s += "IN_ISDIR | ";
|
||||
if(in_event->mask & IN_Q_OVERFLOW) mask_s += "IN_Q_OVERFLOW | ";
|
||||
if(in_event->mask & IN_UNMOUNT) mask_s += "IN_UNMOUNT | ";
|
||||
|
||||
mask_s = mask_s.substr(0, mask_s.size()-3);
|
||||
|
||||
log_d(MODULE, "in_event.wd=%i, mask=%s, name=%s",
|
||||
in_event.wd, cs(mask_s), cs(name));
|
||||
log_d(MODULE, "in_event->wd=%i, mask=%s, name=%s",
|
||||
in_event->wd, cs(mask_s), cs(name));
|
||||
|
||||
if(in_event.mask & IN_IGNORED){
|
||||
if(in_event->mask & IN_IGNORED){
|
||||
// Inotify removed path from watch
|
||||
ss_ path = m_watch_paths[in_event.wd];
|
||||
m_watch_paths.erase(in_event.wd);
|
||||
ss_ path = m_watch_paths[in_event->wd];
|
||||
m_watch_paths.erase(in_event->wd);
|
||||
int r = inotify_add_watch(m_fd, path.c_str(), IN_CLOSE_WRITE |
|
||||
IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE |
|
||||
IN_MODIFY | IN_ATTRIB);
|
||||
@ -118,9 +131,10 @@ struct CMultiFileWatch: MultiFileWatch
|
||||
{
|
||||
struct WatchThing {
|
||||
ss_ path;
|
||||
std::function<void(const ss_ &path)> cb;
|
||||
WatchThing(const ss_ &path, std::function<void(const ss_ &path)> cb):
|
||||
path(path), cb(cb){}
|
||||
sv_<std::function<void(const ss_ &path)>> cbs;
|
||||
WatchThing(const ss_ &path,
|
||||
const sv_<std::function<void(const ss_ &path)>> &cbs):
|
||||
path(path), cbs(cbs){}
|
||||
};
|
||||
|
||||
int m_fd = -1;
|
||||
@ -129,6 +143,7 @@ struct CMultiFileWatch: MultiFileWatch
|
||||
CMultiFileWatch()
|
||||
{
|
||||
m_fd = inotify_init1(IN_NONBLOCK);
|
||||
log_d(MODULE, "CMultiFileWatch(): m_fd=%i", m_fd);
|
||||
if(m_fd == -1){
|
||||
throw Exception(ss_()+"inotify_init() failed: "+strerror(errno));
|
||||
}
|
||||
@ -141,6 +156,15 @@ struct CMultiFileWatch: MultiFileWatch
|
||||
|
||||
void add(const ss_ &path, std::function<void(const ss_ &path)> cb)
|
||||
{
|
||||
for(auto &pair : m_watch){
|
||||
sp_<WatchThing> &watch = pair.second;
|
||||
if(watch->path == path){
|
||||
log_v(MODULE, "Adding callback to path \"%s\" (inotify fd=%i)",
|
||||
cs(path), pair.first);
|
||||
watch->cbs.push_back(cb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
int r = inotify_add_watch(m_fd, path.c_str(), IN_CLOSE_WRITE |
|
||||
IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE |
|
||||
IN_MODIFY | IN_ATTRIB);
|
||||
@ -149,7 +173,7 @@ struct CMultiFileWatch: MultiFileWatch
|
||||
strerror(errno)+" (path="+path+")");
|
||||
}
|
||||
log_v(MODULE, "Watching path \"%s\" (inotify fd=%i)", cs(path), m_fd);
|
||||
m_watch[r] = sp_<WatchThing>(new WatchThing(path, cb));
|
||||
m_watch[r] = sp_<WatchThing>(new WatchThing(path, {cb}));
|
||||
}
|
||||
|
||||
// Used on Linux; no-op on Windows
|
||||
@ -163,47 +187,61 @@ struct CMultiFileWatch: MultiFileWatch
|
||||
{
|
||||
if(fd != m_fd)
|
||||
return;
|
||||
struct inotify_event in_event;
|
||||
char buf[INOTIFY_BUFSIZE];
|
||||
for(;;){
|
||||
int r = read(fd, &in_event, sizeof(struct inotify_event));
|
||||
if(r != sizeof(struct inotify_event))
|
||||
int r = read(fd, buf, INOTIFY_BUFSIZE);
|
||||
if(r == -1){
|
||||
if(errno == EAGAIN)
|
||||
break;
|
||||
log_w(MODULE, "CMultiFileWatch::report_fd(): read() failed "
|
||||
"on fd=%i: %s", fd, strerror(errno));
|
||||
break;
|
||||
sv_<char> name_buf(in_event.len);
|
||||
read(fd, &name_buf[0], in_event.len);
|
||||
ss_ name(name_buf.begin(), name_buf.end());
|
||||
}
|
||||
if(r < (int)INOTIFY_STRUCTSIZE){
|
||||
throw Exception("CMultiFileWatch::report_fd(): read() -> "+itos(r));
|
||||
}
|
||||
struct inotify_event *in_event = (struct inotify_event*)buf;
|
||||
ss_ name;
|
||||
if(r >= (int)INOTIFY_STRUCTSIZE)
|
||||
name = &buf[INOTIFY_STRUCTSIZE]; // Null-terminated string
|
||||
ss_ mask_s;
|
||||
if(in_event.mask & IN_ACCESS) mask_s += "IN_ACCESS | ";
|
||||
if(in_event.mask & IN_ATTRIB) mask_s += "IN_ATTRIB | ";
|
||||
if(in_event.mask & IN_CLOSE_WRITE) mask_s += "IN_CLOSE_WRITE | ";
|
||||
if(in_event.mask & IN_CLOSE_NOWRITE) mask_s += "IN_CLOSE_NOWRITE | ";
|
||||
if(in_event.mask & IN_CREATE) mask_s += "IN_CREATE | ";
|
||||
if(in_event.mask & IN_DELETE) mask_s += "IN_DELETE | ";
|
||||
if(in_event.mask & IN_DELETE_SELF) mask_s += "IN_DELETE_SELF | ";
|
||||
if(in_event.mask & IN_MODIFY) mask_s += "IN_MODIFY | ";
|
||||
if(in_event.mask & IN_MOVE_SELF) mask_s += "IN_MOVE_SELF | ";
|
||||
if(in_event.mask & IN_MOVED_FROM) mask_s += "IN_MOVED_FROM | ";
|
||||
if(in_event.mask & IN_MOVED_TO) mask_s += "IN_MOVED_TO | ";
|
||||
if(in_event.mask & IN_OPEN) mask_s += "IN_OPEN | ";
|
||||
if(in_event.mask & IN_IGNORED) mask_s += "IN_IGNORED | ";
|
||||
if(in_event.mask & IN_ISDIR) mask_s += "IN_ISDIR | ";
|
||||
if(in_event.mask & IN_Q_OVERFLOW) mask_s += "IN_Q_OVERFLOW | ";
|
||||
if(in_event.mask & IN_UNMOUNT) mask_s += "IN_UNMOUNT | ";
|
||||
if(in_event->mask & IN_ACCESS) mask_s += "IN_ACCESS | ";
|
||||
if(in_event->mask & IN_ATTRIB) mask_s += "IN_ATTRIB | ";
|
||||
if(in_event->mask & IN_CLOSE_WRITE) mask_s += "IN_CLOSE_WRITE | ";
|
||||
if(in_event->mask & IN_CLOSE_NOWRITE) mask_s += "IN_CLOSE_NOWRITE | ";
|
||||
if(in_event->mask & IN_CREATE) mask_s += "IN_CREATE | ";
|
||||
if(in_event->mask & IN_DELETE) mask_s += "IN_DELETE | ";
|
||||
if(in_event->mask & IN_DELETE_SELF) mask_s += "IN_DELETE_SELF | ";
|
||||
if(in_event->mask & IN_MODIFY) mask_s += "IN_MODIFY | ";
|
||||
if(in_event->mask & IN_MOVE_SELF) mask_s += "IN_MOVE_SELF | ";
|
||||
if(in_event->mask & IN_MOVED_FROM) mask_s += "IN_MOVED_FROM | ";
|
||||
if(in_event->mask & IN_MOVED_TO) mask_s += "IN_MOVED_TO | ";
|
||||
if(in_event->mask & IN_OPEN) mask_s += "IN_OPEN | ";
|
||||
if(in_event->mask & IN_IGNORED) mask_s += "IN_IGNORED | ";
|
||||
if(in_event->mask & IN_ISDIR) mask_s += "IN_ISDIR | ";
|
||||
if(in_event->mask & IN_Q_OVERFLOW) mask_s += "IN_Q_OVERFLOW | ";
|
||||
if(in_event->mask & IN_UNMOUNT) mask_s += "IN_UNMOUNT | ";
|
||||
|
||||
mask_s = mask_s.substr(0, mask_s.size()-3);
|
||||
|
||||
log_d(MODULE, "in_event.wd=%i, mask=%s, name=%s",
|
||||
in_event.wd, cs(mask_s), cs(name));
|
||||
log_d(MODULE, "in_event->wd=%i, mask=%s, name=%s",
|
||||
in_event->wd, cs(mask_s), cs(name));
|
||||
|
||||
auto it = m_watch.find(in_event.wd);
|
||||
auto it = m_watch.find(in_event->wd);
|
||||
if(it == m_watch.end())
|
||||
throw Exception("inotify returned unknown wd");
|
||||
sp_<WatchThing> thing = it->second;
|
||||
thing->cb(thing->path);
|
||||
for(auto &cb : thing->cbs){
|
||||
if(!name.empty())
|
||||
cb(thing->path+"/"+name);
|
||||
else
|
||||
cb(thing->path);
|
||||
};
|
||||
|
||||
if(in_event.mask & IN_IGNORED){
|
||||
if(in_event->mask & IN_IGNORED){
|
||||
// Inotify removed path from watch
|
||||
const ss_ &path = thing->path;
|
||||
m_watch.erase(in_event.wd);
|
||||
m_watch.erase(in_event->wd);
|
||||
int r = inotify_add_watch(m_fd, path.c_str(), IN_CLOSE_WRITE |
|
||||
IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE |
|
||||
IN_MODIFY | IN_ATTRIB);
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
namespace interface
|
||||
{
|
||||
// TODO: Get some sanity into this; what's up with the virtual methods?
|
||||
struct Filesystem
|
||||
{
|
||||
virtual ~Filesystem(){}
|
||||
@ -21,6 +22,11 @@ namespace interface
|
||||
virtual ss_ get_cwd() = 0;
|
||||
|
||||
virtual ss_ get_absolute_path(const ss_ &path) = 0;
|
||||
|
||||
// "image.png", "png" -> true
|
||||
static bool check_file_extension(const char *path, const char *ext);
|
||||
static ss_ strip_file_extension(const ss_ &path);
|
||||
static ss_ strip_file_name(const ss_ &path);
|
||||
};
|
||||
|
||||
Filesystem* getGlobalFilesystem();
|
||||
|
Loading…
x
Reference in New Issue
Block a user