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