diff --git a/CMakeLists.txt b/CMakeLists.txt index 933161f..b9f6a44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.16) -project(TessesFramework VERSION 1.0.0) +set(TESSESFRAMEWORK_MAJOR_VERSION 0) +set(TESSESFRAMEWORK_MINOR_VERSION 0) +set(TESSESFRAMEWORK_PATCH_VERSION 1) + +project(TessesFramework VERSION ${TESSESFRAMEWORK_MAJOR_VERSION}.${TESSESFRAMEWORK_MINOR_VERSION}.${TESSESFRAMEWORK_PATCH_VERSION}) set(CMAKE_CXX_STANDARD 17) @@ -409,6 +413,11 @@ add_executable(trng apps/trng.cpp) target_link_libraries(trng PUBLIC tessesframework) install(TARGETS trng DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(twatch apps/twatch.cpp) + +target_link_libraries(twatch PUBLIC tessesframework) +install(TARGETS twatch DESTINATION "${CMAKE_INSTALL_BINDIR}") endif() include(InstallRequiredSystemLibraries) diff --git a/Packaging/Linux/PKGBUILD b/Packaging/Linux/PKGBUILD index 6a61e5f..1a7e4e1 100644 --- a/Packaging/Linux/PKGBUILD +++ b/Packaging/Linux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Mike Nolan pkgname=tesses-framework # '-bzr', '-git', '-hg' or '-svn' -pkgver=1.0.0 +pkgver=0.0.1 pkgrel=1 pkgdesc="" arch=('x86_64' 'powerpc') diff --git a/Packaging/Linux/version.sh b/Packaging/Linux/version.sh index 05dcf88..bc77904 100644 --- a/Packaging/Linux/version.sh +++ b/Packaging/Linux/version.sh @@ -1,2 +1,2 @@ -export BUILD=$(($BUILD_NO-163)) -export DEB_VERSION=1.0.0-$BUILD \ No newline at end of file +export BUILD=$(($BUILD_NO-185)) +export DEB_VERSION=0.0.1-$BUILD \ No newline at end of file diff --git a/TessesFrameworkFeatures.h.in b/TessesFrameworkFeatures.h.in index 558552f..2f2cfd5 100644 --- a/TessesFrameworkFeatures.h.in +++ b/TessesFrameworkFeatures.h.in @@ -1,6 +1,11 @@ #pragma once #define TESSES_FRAMEWORK_FLAG_OFF 0 #define TESSES_FRAMEWORK_FLAG_ON 1 + +#define TESSES_FRAMEWORK_MAJOR @TESSESFRAMEWORK_MAJOR_VERSION@ +#define TESSES_FRAMEWORK_MINOR @TESSESFRAMEWORK_MINOR_VERSION@ +#define TESSES_FRAMEWORK_PATCH @TESSESFRAMEWORK_PATCH_VERSION@ + #if defined(TESSES_FRAMEWORK_FLAG_@TESSESFRAMEWORK_ENABLE_SQLITE@) && !defined(TESSESFRAMEWORK_ENABLE_SQLITE) #define TESSESFRAMEWORK_ENABLE_SQLITE #endif diff --git a/apps/twatch.cpp b/apps/twatch.cpp new file mode 100644 index 0000000..e23835d --- /dev/null +++ b/apps/twatch.cpp @@ -0,0 +1,24 @@ +#include + +using namespace Tesses::Framework; +using namespace Tesses::Framework::Filesystem; +using namespace Tesses::Framework::Filesystem::Literals; + +int main(int argc, char** argv) +{ + TF_Init(); + if(argc<2) + { + std::cout << "USAGE " << argv[0] << " " << std::endl; + return 1; + } + + auto watcher=FSWatcher::Create(LocalFS,VFSPath{argv[1]}); + watcher->events = FSWatcherEventType::All; + watcher->event = [](FSWatcherEvent& evt)->void{ + std::cout << evt.ToString() << std::endl; + }; + watcher->SetEnabled(true); + TF_RunEventLoop(); + return 0; +} \ No newline at end of file diff --git a/include/TessesFramework/Common.hpp b/include/TessesFramework/Common.hpp index 9f129fe..05b13e7 100644 --- a/include/TessesFramework/Common.hpp +++ b/include/TessesFramework/Common.hpp @@ -10,6 +10,7 @@ #include #include "Threading/Mutex.hpp" #include +#include namespace Tesses::Framework { diff --git a/include/TessesFramework/Filesystem/LocalFS.hpp b/include/TessesFramework/Filesystem/LocalFS.hpp index b629711..38e8b9b 100644 --- a/include/TessesFramework/Filesystem/LocalFS.hpp +++ b/include/TessesFramework/Filesystem/LocalFS.hpp @@ -42,6 +42,9 @@ namespace Tesses::Framework::Filesystem void Lock(VFSPath path); void Unlock(VFSPath path); + protected: + std::shared_ptr CreateWatcher(std::shared_ptr vfs, VFSPath path); + }; extern std::shared_ptr LocalFS; } \ No newline at end of file diff --git a/include/TessesFramework/Filesystem/VFS.hpp b/include/TessesFramework/Filesystem/VFS.hpp index 908189b..755caed 100644 --- a/include/TessesFramework/Filesystem/VFS.hpp +++ b/include/TessesFramework/Filesystem/VFS.hpp @@ -8,6 +8,7 @@ namespace Tesses::Framework::Filesystem { + class StatVFSData { public: uint64_t BlockSize; @@ -29,7 +30,8 @@ namespace Tesses::Framework::Filesystem static std::vector SplitPath(std::string path); std::vector path; VFSPath(); - + explicit VFSPath(const char* path) : VFSPath(std::string(path)) + {} VFSPath(std::vector path); VFSPath(std::string path); VFSPath(VFSPath p, std::string subent); @@ -118,7 +120,74 @@ namespace Tesses::Framework::Filesystem VFSPathEnumeratorItterator end(); }; + enum class FSWatcherEventType { + None = 0, + //IN_ACCESS + Accessed=1, + //IN_ATTRIB + AttributeChanged =2, + //IN_CLOSE_WRITE + Writen = 4, + //IN_CLOSE_NOWRITE + Read = 8, + //IN_CREATE + Created = 16, + //IN_DELETE + Deleted = 32, + //IN_DELETE_SELF + WatchEntryDeleted = 64, + //IN_MODIFY + Modified = 128, + //IN_MOVE_SELF + WatchEntryMoved = 256, + //IN_MOVED_FROM + MoveOld = 512, + //IN_MOVED_TO + MoveNew = 1024, + //IN_OPEN + Opened = 2048, + //IN_CLOSE + Closed = Writen | Read, + //IN_MOVE + Moved = MoveOld | MoveNew, + //IN_ALL_EVENTS + All = Accessed | AttributeChanged | Created | Deleted | WatchEntryDeleted | Modified | WatchEntryMoved | Opened | Closed | Moved + }; + struct FSWatcherEvent { + //the file or source on move + VFSPath src; + //the dest when moving + VFSPath dest; + FSWatcherEventType type; + bool isDir; + + bool IsEvent(FSWatcherEventType e); + + std::string ToString(); + }; + class VFS; + + class FSWatcher { + private: + std::shared_ptr vfs; + VFSPath path; + protected: + + std::atomic enabled=false; + virtual void SetEnabledImpl(bool enabled); + public: + FSWatcher(std::shared_ptr vfs, VFSPath path); + std::function event; + FSWatcherEventType events = FSWatcherEventType::All; + bool GetEnabled(); + void SetEnabled(bool val); + std::shared_ptr GetFilesystem(); + const VFSPath& GetPath(); + virtual ~FSWatcher() = default; + + static std::shared_ptr Create(std::shared_ptr vfs, VFSPath path); + }; class VFS { public: @@ -156,9 +225,25 @@ namespace Tesses::Framework::Filesystem virtual void Lock(VFSPath path); virtual void Unlock(VFSPath path); - + + virtual ~VFS(); virtual void Close(); + + protected: + virtual std::shared_ptr CreateWatcher(std::shared_ptr vfs, VFSPath path); + friend class FSWatcher; }; -} \ No newline at end of file + + + + namespace Literals + { + inline VFSPath operator""_tpath(const char* path) + { + return VFSPath(path); + } + } +} + diff --git a/src/Filesystem/LocalFS.cpp b/src/Filesystem/LocalFS.cpp index 1a69103..e56d403 100644 --- a/src/Filesystem/LocalFS.cpp +++ b/src/Filesystem/LocalFS.cpp @@ -10,6 +10,13 @@ #include #include #endif + +#include "TessesFramework/Threading/Thread.hpp" +#if defined(__linux__) +#include +#include +#include +#endif namespace Tesses::Framework::Filesystem { #if defined(_WIN32) @@ -249,8 +256,184 @@ namespace Tesses::Framework::Filesystem std::error_code error; std::filesystem::remove(VFSPathToSystem(path),error); } + #if defined(__linux__) + + class INotifyWatcher : public FSWatcher { + std::shared_ptr thrd; + static uint32_t to_linux_mask(FSWatcherEventType flags) + { + uint32_t lflags = 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Accessed) != 0) ? IN_ACCESS : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::AttributeChanged) != 0) ? IN_ATTRIB : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Writen) != 0) ? IN_CLOSE_WRITE : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Read) != 0) ? IN_CLOSE_NOWRITE : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Created) != 0) ? IN_CREATE : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Deleted) != 0) ? IN_DELETE : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::WatchEntryDeleted) != 0) ? IN_DELETE_SELF : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Modified) != 0) ? IN_MODIFY : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::WatchEntryMoved) != 0) ? IN_MOVE_SELF : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveOld) != 0) ? IN_MOVED_FROM : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveNew) != 0) ? IN_MOVED_TO : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Opened) != 0) ? IN_OPEN : 0; + + return lflags; + } + static FSWatcherEventType from_linux_mask(uint32_t lflags) + { + uint32_t flags = 0; + flags |= ((lflags & IN_ACCESS) != 0) ? (uint32_t)FSWatcherEventType::Accessed : 0; + flags |= ((lflags & IN_ATTRIB) != 0) ? (uint32_t)FSWatcherEventType::AttributeChanged : 0; + flags |= ((lflags & IN_CLOSE_WRITE) != 0) ? (uint32_t)FSWatcherEventType::Writen : 0; + flags |= ((lflags & IN_CLOSE_NOWRITE) != 0) ? (uint32_t)FSWatcherEventType::Read : 0; + flags |= ((lflags & IN_CREATE) != 0) ? (uint32_t)FSWatcherEventType::Created : 0; + flags |= ((lflags & IN_DELETE) != 0) ? (uint32_t)FSWatcherEventType::Deleted : 0; + flags |= ((lflags & IN_DELETE_SELF) != 0) ? (uint32_t)FSWatcherEventType::WatchEntryDeleted : 0; + flags |= ((lflags & IN_MODIFY) != 0) ? (uint32_t)FSWatcherEventType::Modified : 0; + flags |= ((lflags & IN_MOVE_SELF) != 0) ? (uint32_t)FSWatcherEventType::WatchEntryMoved : 0; + flags |= ((lflags & IN_MOVED_FROM) != 0) ? (uint32_t)FSWatcherEventType::MoveOld : 0; + flags |= ((lflags & IN_MOVED_TO) != 0) ? (uint32_t)FSWatcherEventType::MoveNew : 0; + flags |= ((lflags & IN_OPEN) != 0) ? (uint32_t)FSWatcherEventType::Opened : 0; + + return (FSWatcherEventType)flags; + } + public: + INotifyWatcher(std::shared_ptr vfs, VFSPath path) : FSWatcher(vfs,path) + { + + } + + protected: + + void SetEnabledImpl(bool enabled) + { + if(enabled) + { + int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd == -1) + { + throw std::runtime_error("Cannot init inotify"); + } + auto str = this->GetFilesystem()->VFSPathToSystem(this->GetPath()); + + int watch = inotify_add_watch(fd, str.c_str(),to_linux_mask(this->events)); + + thrd = std::make_shared([this,watch,fd]()-> void { + int cnt = 0; + struct pollfd pfd = {.fd = fd, .events = POLLIN}; + std::vector> mvFroms; + char buf[4096] + __attribute__ ((aligned(__alignof__(struct inotify_event)))); + const struct inotify_event *event; + ssize_t size; + + bool fail=false; + + FSWatcherEvent evt; + evt.dest = this->GetPath(); + while(!fail && this->enabled) + { + cnt = poll(&pfd,1,-1); + if(cnt == -1) break; + + if(cnt > 0) + { + if(pfd.revents & POLLIN) + { + for (;;) { + size = read(fd, buf, sizeof(buf)); + if (size == -1 && errno != EAGAIN) { + fail=true; + break; + } + + if (size <= 0) + break; + + for (char *ptr = buf; ptr < buf + size; + ptr += sizeof(struct inotify_event) + event->len) { + + event = (const struct inotify_event *) ptr; + VFSPath path = this->GetPath(); + + if(event->len) + path = path / std::string(event->name, (size_t)event->len); + + if(((uint32_t)this->events & (uint32_t)FSWatcherEventType::Moved) == (uint32_t)FSWatcherEventType::Moved && event->mask & IN_MOVED_FROM) + { + mvFroms.emplace_back(path,event->cookie); + } + else if(((uint32_t)this->events & (uint32_t)FSWatcherEventType::Moved) == (uint32_t)FSWatcherEventType::Moved && event->mask & IN_MOVED_TO) + { + for(auto ittr = mvFroms.begin(); ittr != mvFroms.end(); ittr++) + { + if(ittr->second == event->cookie) + { + evt.src = ittr->first; + mvFroms.erase(ittr); + break; + } + } + evt.isDir = (event->mask & IN_ISDIR); + evt.dest = path; + evt.type = FSWatcherEventType::Moved; + if(this->event) + this->event(evt); + } + else { + + evt.isDir = (event->mask & IN_ISDIR); + evt.src = path; + evt.type = from_linux_mask(event->mask);; + if(this->event) + this->event(evt); + } + if(event->mask & IN_MOVE_SELF) + { + close(fd); + return; + } + if(event->mask & IN_DELETE_SELF) + { + close(fd); + return; + } + } + } + } + } + } + + close(fd); + }); + + + } + else + { + thrd = nullptr; + } + } + public: + ~INotifyWatcher() + { + this->enabled = false; + } + }; + #endif + + + + std::shared_ptr LocalFilesystem::CreateWatcher(std::shared_ptr vfs, VFSPath path) + { + #if defined(__linux__) + return std::make_shared(vfs, path); + #endif + return VFS::CreateWatcher(vfs,path); + } std::shared_ptr LocalFS = std::make_shared(); + + } // C:/Users/Jim/Joel diff --git a/src/Filesystem/VFS.cpp b/src/Filesystem/VFS.cpp index 21afce9..60e0cc9 100644 --- a/src/Filesystem/VFS.cpp +++ b/src/Filesystem/VFS.cpp @@ -232,7 +232,8 @@ namespace Tesses::Framework::Filesystem if(i == this->path.size()-1 && i == toMakeRelativeTo.path.size()-1) { - VFSPath path({this->path[this->path.size()-1]}); + std::vector paths{this->path[this->path.size()-1]}; + VFSPath path(paths); path.relative = true; return path; } @@ -387,7 +388,8 @@ namespace Tesses::Framework::Filesystem if(ext.empty()) return; if(ext[0] != '.') { - str += '.' + ext; + str += '.'; + str += ext; } else { @@ -560,4 +562,113 @@ namespace Tesses::Framework::Filesystem { } -} \ No newline at end of file + + + + std::shared_ptr VFS::CreateWatcher(std::shared_ptr vfs,VFSPath path) + { + return std::make_shared(vfs,path); + } + void FSWatcher::SetEnabled(bool enabled) + { + if(this->enabled == enabled) return; + this->enabled = enabled; + this->SetEnabledImpl(enabled); + } + + bool FSWatcher::GetEnabled() + { + return this->enabled; + } + + void FSWatcher::SetEnabledImpl(bool enabled) + { + + } + + std::shared_ptr FSWatcher::GetFilesystem() + { + return this->vfs; + } + const VFSPath& FSWatcher::GetPath() + { + return this->path; + } + + FSWatcher::FSWatcher(std::shared_ptr vfs, VFSPath path): vfs(vfs), path(path) + { + + } + + std::shared_ptr FSWatcher::Create(std::shared_ptr vfs, VFSPath path) + { + return vfs->CreateWatcher(vfs,path); + } + + bool FSWatcherEvent::IsEvent(FSWatcherEventType e) + { + if(e == FSWatcherEventType::All) return this->type != FSWatcherEventType::None; + if(e == FSWatcherEventType::Moved) return ((uint32_t)this->type & (uint32_t)FSWatcherEventType::Moved) == (uint32_t)FSWatcherEventType::Moved; + if(e == FSWatcherEventType::Closed) return ((uint32_t)this->type & (uint32_t)FSWatcherEventType::Closed) != 0; + return (uint32_t)this->type & (uint32_t)e; + } + + std::string FSWatcherEvent::ToString() + { + if(IsEvent(FSWatcherEventType::Moved)) + { + return (this->isDir ? "Moved directory " : "Moved file ") + this->src.ToString() + " -> " + this->dest.ToString(); + } + else if(IsEvent(FSWatcherEventType::MoveOld)) + { + return (this->isDir ? "Move source directory " : "Move source file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::MoveNew)) + { + return (this->isDir ? "Move destination directory " : "Move destination file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::Accessed)) + { + return (this->isDir ? "Accessed directory " : "Accessed file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::AttributeChanged)) + { + return (this->isDir ? "Changed attr on directory " : "Changed attr on file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::Writen)) + { + return (this->isDir ? "Finished changing directory " : "Finished writing to file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::Read)) + { + return (this->isDir ? "Finished reading directory " : "Finished reading from file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::Created)) + { + return (this->isDir ? "Created directory " : "Created file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::Deleted)) + { + return (this->isDir ? "Deleted directory " : "Deleted file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::WatchEntryDeleted)) + { + + return (this->isDir ? "Deleted watched directory " : "Deleted watched file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::Modified)) + { + return (this->isDir ? "Modified directory " : "Modified file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::WatchEntryMoved)) + { + return (this->isDir ? "Moved watched directory " : "Moved watched file ") + this->src.ToString(); + } + else if(IsEvent(FSWatcherEventType::Opened)) + { + return (this->isDir ? "Opened directory " : "Opened file ") + this->src.ToString(); + } + + return ""; + } +} \ No newline at end of file