|
@@ -0,0 +1,290 @@
|
|
|
|
+#include <fcntl.h>
|
|
|
|
+#include <unistd.h>
|
|
|
|
+#include <string.h>
|
|
|
|
+#include <poll.h>
|
|
|
|
+#include <signal.h>
|
|
|
|
+#include <errno.h>
|
|
|
|
+#include <limits.h>
|
|
|
|
+#include "gfainotify.h"
|
|
|
|
+
|
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+#define _THREAD_CMD_WAIT_COND 0
|
|
|
|
+#define _THREAD_CMD_EXIT 1
|
|
|
|
+
|
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+CInotify::CInotify(void) : m_fdIn(-1),
|
|
|
|
+ m_tCond(PTHREAD_COND_INITIALIZER),
|
|
|
|
+ m_mtx(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP),
|
|
|
|
+ m_fdReadPipe(m_fdPipe[0]),
|
|
|
|
+ m_fdWritePipe(m_fdPipe[1]),
|
|
|
|
+ m_bThreadRunning(false)
|
|
|
|
+{
|
|
|
|
+ m_fdReadPipe = m_fdWritePipe = -1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+CInotify::~CInotify(void)
|
|
|
|
+{
|
|
|
|
+ Close();
|
|
|
|
+ ::pthread_cond_destroy(&m_tCond);
|
|
|
|
+ ::pthread_mutex_destroy(&m_mtx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+void CInotify::LockMutex(void)
|
|
|
|
+{
|
|
|
|
+ ::pthread_mutex_lock(&m_mtx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CInotify::UnlockMutex(void)
|
|
|
|
+{
|
|
|
|
+ ::pthread_mutex_unlock(&m_mtx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+bool CInotify::Init(void)
|
|
|
|
+{
|
|
|
|
+ bool bRet = true;
|
|
|
|
+
|
|
|
|
+ if(m_fdIn >= 0)
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ if(m_fdIn == -1)
|
|
|
|
+ {
|
|
|
|
+ LockMutex();
|
|
|
|
+ if(::pipe(m_fdPipe) >= 0)
|
|
|
|
+ {
|
|
|
|
+ if((m_fdIn = ::inotify_init1(IN_NONBLOCK)) >= 0)
|
|
|
|
+ {
|
|
|
|
+ m_thread.Create(&CInotify::WatchThreadRoutine, reinterpret_cast<void*>(this));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ bRet = (m_fdIn >= 0);
|
|
|
|
+ UnlockMutex();
|
|
|
|
+
|
|
|
|
+ while(!m_bThreadRunning)
|
|
|
|
+ ::pthread_yield();
|
|
|
|
+ }
|
|
|
|
+ return bRet;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CInotify::Close(void)
|
|
|
|
+{
|
|
|
|
+ if(!SuspendMonitorThread(_THREAD_CMD_EXIT))
|
|
|
|
+ {
|
|
|
|
+ errno = EPERM;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LockMutex();
|
|
|
|
+ if(m_fdIn >= 0)
|
|
|
|
+ {
|
|
|
|
+ ::close(m_fdIn);
|
|
|
|
+ m_fdIn = -1;
|
|
|
|
+ }
|
|
|
|
+ m_thread.Join(NULL);
|
|
|
|
+ if(m_fdReadPipe >= 0)
|
|
|
|
+ {
|
|
|
|
+ close(m_fdReadPipe);
|
|
|
|
+ m_fdReadPipe = -1;
|
|
|
|
+ }
|
|
|
|
+ if(m_fdWritePipe >= 0)
|
|
|
|
+ {
|
|
|
|
+ close(m_fdWritePipe);
|
|
|
|
+ m_fdWritePipe = -1;
|
|
|
|
+ }
|
|
|
|
+ UnlockMutex();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+bool CInotify::SuspendMonitorThread(int sig)
|
|
|
|
+{
|
|
|
|
+ if(m_thread.GetID() == ::pthread_self())
|
|
|
|
+ return true;
|
|
|
|
+ if(m_fdWritePipe >= 0)
|
|
|
|
+ return write(m_fdWritePipe, &sig, sizeof(sig)) == sizeof(sig);
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CInotify::ResumeMonitorThread(void)
|
|
|
|
+{
|
|
|
|
+ if(m_thread.GetID() != ::pthread_self())
|
|
|
|
+ {
|
|
|
|
+ if(m_fdIn >= 0 && m_fdReadPipe >= 0)
|
|
|
|
+ {
|
|
|
|
+ LockMutex();
|
|
|
|
+ ::pthread_cond_signal(&m_tCond);
|
|
|
|
+ UnlockMutex();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool CInotify::SignalMonitorThread(int sig)
|
|
|
|
+{
|
|
|
|
+ return write(m_fdWritePipe, &sig, sizeof(sig)) == sizeof(sig);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CInotify::WaitCondition(void)
|
|
|
|
+{
|
|
|
|
+ ::pthread_cond_wait(&m_tCond, &m_mtx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+int CInotify::AddWatch(const char *pszFilepath, uint32_t mask, PFN_WATCH_EVENT_CALLBACK pfnCb, void *pParam)
|
|
|
|
+{
|
|
|
|
+ int wd;
|
|
|
|
+ if(!pszFilepath || !*pszFilepath || !pfnCb)
|
|
|
|
+ {
|
|
|
|
+ errno = EINVAL;
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(!SuspendMonitorThread(_THREAD_CMD_WAIT_COND))
|
|
|
|
+ {
|
|
|
|
+ errno = EINVAL;
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LockMutex();
|
|
|
|
+ if((wd = ::inotify_add_watch(m_fdIn, pszFilepath, mask)) >= 0)
|
|
|
|
+ {
|
|
|
|
+ INOTIFY_WATCH iw;
|
|
|
|
+ iw.wd = wd;
|
|
|
|
+ iw.mask = mask;
|
|
|
|
+ iw.pfnCb = pfnCb;
|
|
|
|
+ iw.pParam = pParam;
|
|
|
|
+ m_vWatches.push_back(iw);
|
|
|
|
+ }
|
|
|
|
+ UnlockMutex();
|
|
|
|
+ ResumeMonitorThread();
|
|
|
|
+ return wd;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int CInotify::RemoveWatch(int wd)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if(!SuspendMonitorThread(_THREAD_CMD_WAIT_COND))
|
|
|
|
+ {
|
|
|
|
+ errno = EINVAL;
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LockMutex();
|
|
|
|
+ ret = ::inotify_rm_watch(m_fdIn, wd);
|
|
|
|
+ RemoveWatchByDescriptor(wd);
|
|
|
|
+ UnlockMutex();
|
|
|
|
+ ResumeMonitorThread();
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CInotify::RemoveWatchByDescriptor(int wd)
|
|
|
|
+{
|
|
|
|
+ for(auto i = m_vWatches.begin(); i != m_vWatches.end(); ++i)
|
|
|
|
+ {
|
|
|
|
+ const INOTIFY_WATCH &iw = *i;
|
|
|
|
+ if(iw.wd == wd)
|
|
|
|
+ {
|
|
|
|
+ m_vWatches.erase(i);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+LPCINOTIFY_WATCH CInotify::FindByWatchDescriptor(int wd) const
|
|
|
|
+{
|
|
|
|
+ for(auto i = m_vWatches.begin(); i != m_vWatches.end(); ++i)
|
|
|
|
+ {
|
|
|
|
+ const INOTIFY_WATCH &iw = *i;
|
|
|
|
+ if(iw.wd == wd)
|
|
|
|
+ return &iw;
|
|
|
|
+ }
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+void* CInotify::WatchThreadRoutine(void *pParam)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ bool bRun = true;
|
|
|
|
+ struct pollfd pfd[2];
|
|
|
|
+ CInotify *pThis = reinterpret_cast<CInotify*>(pParam);
|
|
|
|
+ char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
|
|
|
|
+
|
|
|
|
+ memset(pfd, 0, sizeof(pfd));
|
|
|
|
+ pfd[0].events = POLLIN;
|
|
|
|
+ pfd[1].events = POLLIN;
|
|
|
|
+
|
|
|
|
+ pThis->LockMutex();
|
|
|
|
+ pThis->m_bThreadRunning = true;
|
|
|
|
+
|
|
|
|
+ do
|
|
|
|
+ {
|
|
|
|
+ if((pThis->m_fdIn >= 0) && (pThis->m_fdReadPipe >= 0))
|
|
|
|
+ {
|
|
|
|
+ pfd[0].fd = pThis->m_fdReadPipe;
|
|
|
|
+ pfd[1].fd = pThis->m_fdIn;
|
|
|
|
+
|
|
|
|
+ if((ret = poll(pfd, 2, -1)) > 0)
|
|
|
|
+ {
|
|
|
|
+ if(pfd[1].revents & POLLIN)
|
|
|
|
+ {
|
|
|
|
+ do
|
|
|
|
+ {
|
|
|
|
+ ret = read(pThis->m_fdIn, buf, sizeof(buf));
|
|
|
|
+
|
|
|
|
+ if(ret <= 0)
|
|
|
|
+ {
|
|
|
|
+ if(errno != EAGAIN)
|
|
|
|
+ bRun = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LPCINOTIFY_WATCH pnw;
|
|
|
|
+ const struct inotify_event *pEvt = NULL;
|
|
|
|
+
|
|
|
|
+ for(char *ptr = buf; ptr < (buf + ret); ptr += (sizeof(struct inotify_event) + pEvt->len))
|
|
|
|
+ {
|
|
|
|
+ pEvt = (const struct inotify_event*)ptr;
|
|
|
|
+ if((pnw = pThis->FindByWatchDescriptor(pEvt->wd)))
|
|
|
|
+ {
|
|
|
|
+ pThis->UnlockMutex();
|
|
|
|
+ (*pnw->pfnCb)(*pEvt, pnw->pParam);
|
|
|
|
+ pThis->LockMutex();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ while(ret > 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(pfd[0].revents & POLLIN)
|
|
|
|
+ {
|
|
|
|
+ int cmd;
|
|
|
|
+ if((ret = read(pThis->m_fdReadPipe, &cmd, sizeof(cmd))) == sizeof(cmd))
|
|
|
|
+ {
|
|
|
|
+ switch(cmd)
|
|
|
|
+ {
|
|
|
|
+ case _THREAD_CMD_WAIT_COND:
|
|
|
|
+ pThis->WaitCondition();
|
|
|
|
+ break;
|
|
|
|
+ case _THREAD_CMD_EXIT:
|
|
|
|
+ bRun = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ while(bRun);
|
|
|
|
+
|
|
|
|
+ pThis->m_bThreadRunning = false;
|
|
|
|
+ pThis->UnlockMutex();
|
|
|
|
+ return NULL;
|
|
|
|
+}
|