diff options
Diffstat (limited to 'shared/inotify/inotify-cxx.cpp')
-rw-r--r-- | shared/inotify/inotify-cxx.cpp | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/shared/inotify/inotify-cxx.cpp b/shared/inotify/inotify-cxx.cpp new file mode 100644 index 00000000..7870e825 --- /dev/null +++ b/shared/inotify/inotify-cxx.cpp @@ -0,0 +1,630 @@ + +/// inotify C++ interface implementation +/** + * \file inotify-cxx.cpp + * + * inotify C++ interface + * + * Copyright (C) 2006, 2007 Lukas Jelinek <lukas@aiken.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of one of the following licenses: + * + * \li 1. X11-style license (see LICENSE-X11) + * \li 2. GNU Lesser General Public License, version 2.1 (see LICENSE-LGPL) + * \li 3. GNU General Public License, version 2 (see LICENSE-GPL) + * + * If you want to help with choosing the best license for you, + * please visit http://www.gnu.org/licenses/license-list.html. + * + */ + + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include "inotify-cxx.h" + +/// procfs inotify base path +#define PROCFS_INOTIFY_BASE "/proc/sys/fs/inotify/" + +/// dump separator (between particular entries) +#define DUMP_SEP \ + ({ \ + if (!rStr.empty()) { \ + rStr.append(","); \ + } \ + }) + + + +int32_t InotifyEvent::GetDescriptor() const +{ + return m_pWatch != NULL // if watch exists + ? m_pWatch->GetDescriptor() // return its descriptor + : -1; // else return -1 +} + +uint32_t InotifyEvent::GetMaskByName(const std::string& rName) +{ + if (rName == "IN_ACCESS") + return IN_ACCESS; + else if (rName == "IN_MODIFY") + return IN_MODIFY; + else if (rName == "IN_ATTRIB") + return IN_ATTRIB; + else if (rName == "IN_CLOSE_WRITE") + return IN_CLOSE_WRITE; + else if (rName == "IN_CLOSE_NOWRITE") + return IN_CLOSE_NOWRITE; + else if (rName == "IN_OPEN") + return IN_OPEN; + else if (rName == "IN_MOVED_FROM") + return IN_MOVED_FROM; + else if (rName == "IN_MOVED_TO") + return IN_MOVED_TO; + else if (rName == "IN_CREATE") + return IN_CREATE; + else if (rName == "IN_DELETE") + return IN_DELETE; + else if (rName == "IN_DELETE_SELF") + return IN_DELETE_SELF; + else if (rName == "IN_UNMOUNT") + return IN_UNMOUNT; + else if (rName == "IN_Q_OVERFLOW") + return IN_Q_OVERFLOW; + else if (rName == "IN_IGNORED") + return IN_IGNORED; + else if (rName == "IN_CLOSE") + return IN_CLOSE; + else if (rName == "IN_MOVE") + return IN_MOVE; + else if (rName == "IN_ISDIR") + return IN_ISDIR; + else if (rName == "IN_ONESHOT") + return IN_ONESHOT; + else if (rName == "IN_ALL_EVENTS") + return IN_ALL_EVENTS; + +#ifdef IN_DONT_FOLLOW + else if (rName == "IN_DONT_FOLLOW") + return IN_DONT_FOLLOW; +#endif // IN_DONT_FOLLOW + +#ifdef IN_ONLYDIR + else if (rName == "IN_ONLYDIR") + return IN_ONLYDIR; +#endif // IN_ONLYDIR + +#ifdef IN_MOVE_SELF + else if (rName == "IN_MOVE_SELF") + return IN_MOVE_SELF; +#endif // IN_MOVE_SELF + + return (uint32_t) 0; +} + +void InotifyEvent::DumpTypes(uint32_t uValue, std::string& rStr) +{ + rStr = ""; + + if (IsType(uValue, IN_ALL_EVENTS)) { + rStr.append("IN_ALL_EVENTS"); + } + else { + if (IsType(uValue, IN_ACCESS)) { + DUMP_SEP; + rStr.append("IN_ACCESS"); + } + if (IsType(uValue, IN_MODIFY)) { + DUMP_SEP; + rStr.append("IN_MODIFY"); + } + if (IsType(uValue, IN_ATTRIB)) { + DUMP_SEP; + rStr.append("IN_ATTRIB"); + } + if (IsType(uValue, IN_CREATE)) { + DUMP_SEP; + rStr.append("IN_CREATE"); + } + if (IsType(uValue, IN_DELETE)) { + DUMP_SEP; + rStr.append("IN_DELETE"); + } + if (IsType(uValue, IN_DELETE_SELF)) { + DUMP_SEP; + rStr.append("IN_DELETE_SELF"); + } + if (IsType(uValue, IN_OPEN)) { + DUMP_SEP; + rStr.append("IN_OPEN"); + } + if (IsType(uValue, IN_CLOSE)) { + DUMP_SEP; + rStr.append("IN_CLOSE"); + } + +#ifdef IN_MOVE_SELF + if (IsType(uValue, IN_MOVE_SELF)) { + DUMP_SEP; + rStr.append("IN_MOVE_SELF"); + } +#endif // IN_MOVE_SELF + + else { + if (IsType(uValue, IN_CLOSE_WRITE)) { + DUMP_SEP; + rStr.append("IN_CLOSE_WRITE"); + } + if (IsType(uValue, IN_CLOSE_NOWRITE)) { + DUMP_SEP; + rStr.append("IN_CLOSE_NOWRITE"); + } + } + if (IsType(uValue, IN_MOVE)) { + DUMP_SEP; + rStr.append("IN_MOVE"); + } + else { + if (IsType(uValue, IN_MOVED_FROM)) { + DUMP_SEP; + rStr.append("IN_MOVED_FROM"); + } + if (IsType(uValue, IN_MOVED_TO)) { + DUMP_SEP; + rStr.append("IN_MOVED_TO"); + } + } + } + if (IsType(uValue, IN_UNMOUNT)) { + DUMP_SEP; + rStr.append("IN_UNMOUNT"); + } + if (IsType(uValue, IN_Q_OVERFLOW)) { + DUMP_SEP; + rStr.append("IN_Q_OVERFLOW"); + } + if (IsType(uValue, IN_IGNORED)) { + DUMP_SEP; + rStr.append("IN_IGNORED"); + } + if (IsType(uValue, IN_ISDIR)) { + DUMP_SEP; + rStr.append("IN_ISDIR"); + } + if (IsType(uValue, IN_ONESHOT)) { + DUMP_SEP; + rStr.append("IN_ONESHOT"); + } + +#ifdef IN_DONT_FOLLOW + if (IsType(uValue, IN_DONT_FOLLOW)) { + DUMP_SEP; + rStr.append("IN_DONT_FOLLOW"); + } +#endif // IN_DONT_FOLLOW + +#ifdef IN_ONLYDIR + if (IsType(uValue, IN_ONLYDIR)) { + DUMP_SEP; + rStr.append("IN_ONLYDIR"); + } +#endif // IN_ONLYDIR +} + +void InotifyEvent::DumpTypes(std::string& rStr) const +{ + DumpTypes(m_uMask, rStr); +} + + +void InotifyWatch::SetMask(uint32_t uMask) throw (InotifyException) +{ + IN_WRITE_BEGIN + + if (m_wd != -1) { + int wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), uMask); + if (wd != m_wd) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("changing mask failed"), wd == -1 ? errno : EINVAL, this); + } + } + + m_uMask = uMask; + + IN_WRITE_END +} + +void InotifyWatch::SetEnabled(bool fEnabled) throw (InotifyException) +{ + IN_WRITE_BEGIN + + if (fEnabled == m_fEnabled) { + IN_WRITE_END_NOTHROW + return; + } + + if (m_pInotify != NULL) { + if (fEnabled) { + m_wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), m_uMask); + if (m_wd == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("enabling watch failed"), errno, this); + } + m_pInotify->m_watches.insert(IN_WATCH_MAP::value_type(m_wd, this)); + } + else { + if (inotify_rm_watch(m_pInotify->GetDescriptor(), m_wd) != 0) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("disabling watch failed"), errno, this); + } + m_pInotify->m_watches.erase(m_wd); + m_wd = -1; + } + } + + m_fEnabled = fEnabled; + + IN_WRITE_END +} + +void InotifyWatch::__Disable() +{ + IN_WRITE_BEGIN + + if (!m_fEnabled) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("event cannot occur on disabled watch"), EINVAL, this); + } + + if (m_pInotify != NULL) { + m_pInotify->m_watches.erase(m_wd); + m_wd = -1; + } + + m_fEnabled = false; + + IN_WRITE_END +} + + +Inotify::Inotify() throw (InotifyException) +{ + IN_LOCK_INIT + + m_fd = inotify_init(); + if (m_fd == -1) { + IN_LOCK_DONE + throw InotifyException(IN_EXC_MSG("inotify init failed"), errno, NULL); + } +} + +Inotify::~Inotify() +{ + Close(); + + IN_LOCK_DONE +} + +void Inotify::Close() +{ + IN_WRITE_BEGIN + + if (m_fd != -1) { + RemoveAll(); + close(m_fd); + m_fd = -1; + } + + IN_WRITE_END +} + +void Inotify::Add(InotifyWatch* pWatch) throw (InotifyException) +{ + IN_WRITE_BEGIN + + // invalid descriptor - this case shouldn't occur - go away + if (m_fd == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this); + } + + // this path already watched - go away + if (FindWatch(pWatch->GetPath()) != NULL) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("path already watched"), EBUSY, this); + } + + // for enabled watch + if (pWatch->IsEnabled()) { + + // try to add watch to kernel + int wd = inotify_add_watch(m_fd, pWatch->GetPath().c_str(), pWatch->GetMask()); + + // adding failed - go away + if (wd == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("adding watch failed"), errno, this); + } + + // this path already watched (but defined another way) + InotifyWatch* pW = FindWatch(wd); + if (pW != NULL) { + + // try to recover old watch because it may be modified - then go away + if (inotify_add_watch(m_fd, pW->GetPath().c_str(), pW->GetMask()) < 0) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("watch collision detected and recovery failed"), errno, this); + } + else { + // recovery failed - go away + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("path already watched (but defined another way)"), EBUSY, this); + } + } + + pWatch->m_wd = wd; + m_watches.insert(IN_WATCH_MAP::value_type(pWatch->m_wd, pWatch)); + } + + m_paths.insert(IN_WP_MAP::value_type(pWatch->m_path, pWatch)); + pWatch->m_pInotify = this; + + IN_WRITE_END +} + +void Inotify::Remove(InotifyWatch* pWatch) throw (InotifyException) +{ + IN_WRITE_BEGIN + + // invalid descriptor - this case shouldn't occur - go away + if (m_fd == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this); + } + + // for enabled watch + if (pWatch->m_wd != -1) { + + // removing watch failed - go away + if (inotify_rm_watch(m_fd, pWatch->m_wd) == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("removing watch failed"), errno, this); + } + m_watches.erase(pWatch->m_wd); + pWatch->m_wd = -1; + } + + m_paths.erase(pWatch->m_path); + pWatch->m_pInotify = NULL; + + IN_WRITE_END +} + +void Inotify::RemoveAll() +{ + IN_WRITE_BEGIN + + IN_WP_MAP::iterator it = m_paths.begin(); + while (it != m_paths.end()) { + InotifyWatch* pW = (*it).second; + if (pW->m_wd != -1) { + inotify_rm_watch(m_fd, pW->m_wd); + pW->m_wd = -1; + } + pW->m_pInotify = NULL; + it++; + } + + m_watches.clear(); + m_paths.clear(); + + IN_WRITE_END +} + +void Inotify::WaitForEvents(bool fNoIntr) throw (InotifyException) +{ + ssize_t len = 0; + + do { + len = read(m_fd, m_buf, INOTIFY_BUFLEN); + } while (fNoIntr && len == -1 && errno == EINTR); + + if (len == -1 && !(errno == EWOULDBLOCK || errno == EINTR)) + throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this); + + if (len == -1) + return; + + IN_WRITE_BEGIN + + ssize_t i = 0; + while (i < len) { + struct inotify_event* pEvt = (struct inotify_event*) &m_buf[i]; + InotifyWatch* pW = FindWatch(pEvt->wd); + if (pW != NULL) { + InotifyEvent evt(pEvt, pW); + if ( InotifyEvent::IsType(pW->GetMask(), IN_ONESHOT) + || InotifyEvent::IsType(evt.GetMask(), IN_IGNORED)) + pW->__Disable(); + m_events.push_back(evt); + } + i += INOTIFY_EVENT_SIZE + (ssize_t) pEvt->len; + } + + IN_WRITE_END +} + +bool Inotify::GetEvent(InotifyEvent* pEvt) throw (InotifyException) +{ + if (pEvt == NULL) + throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this); + + IN_WRITE_BEGIN + + bool b = !m_events.empty(); + if (b) { + *pEvt = m_events.front(); + m_events.pop_front(); + } + + IN_WRITE_END + + return b; +} + +bool Inotify::PeekEvent(InotifyEvent* pEvt) throw (InotifyException) +{ + if (pEvt == NULL) + throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this); + + IN_READ_BEGIN + + bool b = !m_events.empty(); + if (b) { + *pEvt = m_events.front(); + } + + IN_READ_END + + return b; +} + +InotifyWatch* Inotify::FindWatch(int iDescriptor) +{ + IN_READ_BEGIN + + IN_WATCH_MAP::iterator it = m_watches.find(iDescriptor); + InotifyWatch* pW = it == m_watches.end() ? NULL : (*it).second; + + IN_READ_END + + return pW; +} + +InotifyWatch* Inotify::FindWatch(const std::string& rPath) +{ + IN_READ_BEGIN + + IN_WP_MAP::iterator it = m_paths.find(rPath); + InotifyWatch* pW = it == m_paths.end() ? NULL : (*it).second; + + IN_READ_END + + return pW; +} + +void Inotify::SetNonBlock(bool fNonBlock) throw (InotifyException) +{ + IN_WRITE_BEGIN + + if (m_fd == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this); + } + + int res = fcntl(m_fd, F_GETFL); + if (res == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this); + } + + if (fNonBlock) { + res |= O_NONBLOCK; + } + else { + res &= ~O_NONBLOCK; + } + + if (fcntl(m_fd, F_SETFL, res) == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this); + } + + IN_WRITE_END +} + +void Inotify::SetCloseOnExec(bool fClOnEx) throw (InotifyException) +{ + IN_WRITE_BEGIN + + if (m_fd == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this); + } + + int res = fcntl(m_fd, F_GETFD); + if (res == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this); + } + + if (fClOnEx) { + res |= FD_CLOEXEC; + } + else { + res &= ~FD_CLOEXEC; + } + + if (fcntl(m_fd, F_SETFD, res) == -1) { + IN_WRITE_END_NOTHROW + throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this); + } + + IN_WRITE_END +} + +uint32_t Inotify::GetCapability(InotifyCapability_t cap) throw (InotifyException) +{ + FILE* f = fopen(GetCapabilityPath(cap).c_str(), "r"); + if (f == NULL) + throw InotifyException(IN_EXC_MSG("cannot get capability"), errno, NULL); + + unsigned int val = 0; + if (fscanf(f, "%u", &val) != 1) { + fclose(f); + throw InotifyException(IN_EXC_MSG("cannot get capability"), EIO, NULL); + } + + fclose(f); + + return (uint32_t) val; +} + +void Inotify::SetCapability(InotifyCapability_t cap, uint32_t val) throw (InotifyException) +{ + FILE* f = fopen(GetCapabilityPath(cap).c_str(), "w"); + if (f == NULL) + throw InotifyException(IN_EXC_MSG("cannot set capability"), errno, NULL); + + if (fprintf(f, "%u", (unsigned int) val) <= 0) { + fclose(f); + throw InotifyException(IN_EXC_MSG("cannot set capability"), EIO, NULL); + } + + fclose(f); +} + +std::string Inotify::GetCapabilityPath(InotifyCapability_t cap) throw (InotifyException) +{ + std::string path(PROCFS_INOTIFY_BASE); + + switch (cap) { + case IN_MAX_EVENTS: + path.append("max_queued_events"); + break; + case IN_MAX_INSTANCES: + path.append("max_user_instances"); + break; + case IN_MAX_WATCHES: + path.append("max_user_watches"); + break; + default: + throw InotifyException(IN_EXC_MSG("unknown capability type"), EINVAL, NULL); + } + + return path; +} + |