public:
EventFD();
- ~EventFD();
+ virtual ~EventFD();
EventFD(const EventFD& eventfd) = delete;
EventFD& operator=(const EventFD&) = delete;
return ::stat(path.c_str(), &s) == 0 && S_IFCHR == (s.st_mode & S_IFMT);
}
+void assertIsDir(const std::string& path)
+{
+ if (path.empty()) {
+ const std::string msg = "Empty path";
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+
+ struct stat s;
+ if (::stat(path.c_str(), &s)) {
+ const std::string msg = "Error in stat() " + path + ": " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+
+ if(!(s.st_mode & S_IFDIR)) {
+ const std::string msg = "Not a directory";
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+
+ if(::access(path.c_str(), X_OK) < 0) {
+ const std::string msg = "Not a traversable directory";
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+}
+
+
namespace {
// NOTE: Should be the same as in systemd/src/core/mount-setup.c
const std::string RUN_MOUNT_POINT_OPTIONS = "mode=755,smackfstransmute=System::Run";
{
try {
for (fs::directory_iterator file(src);
- file != fs::directory_iterator();
- ++file) {
+ file != fs::directory_iterator();
+ ++file) {
fs::path current(file->path());
fs::path destination = dst / current.filename();
fs::remove(*iter, errorCode);
if (errorCode) {
LOGE("Error during cleaning: dir: " << *iter
- << ", msg: " << errorCode.message());
+ << ", msg: " << errorCode.message());
}
}
return false;
bool createFifo(const std::string& path, mode_t mode)
{
- int ret = ::mkfifo(path.c_str(), mode);
- if (ret < 0) {
- LOGE("Failed to make fifo: path=host:" << path);
- return false;
- }
- return true;
+ int ret = ::mkfifo(path.c_str(), mode);
+ if (ret < 0) {
+ LOGE("Failed to make fifo: path=host:" << path);
+ return false;
+ }
+ return true;
}
bool copyFile(const std::string& src, const std::string& dest)
bool retSmack = copySmackLabel(src, dest);
if (!retSmack) {
LOGE("Failed to copy smack label: path=host:"
- << src
- << ", path=host:"
- << dest);
+ << src
+ << ", path=host:"
+ << dest);
boost::system::error_code ec;
fs::remove(dest, ec);
if (!ec) {
bool isCharDevice(const std::string& path);
/**
+ * Checks if a path exists and points to a directory
+ */
+void assertIsDir(const std::string& path);
+
+/**
* List all (including '.' and '..' entries) dir entries
*/
bool listDir(const std::string& path, std::vector<std::string>& files);
--- /dev/null
+/*
+* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Contact: Jan Olszak <j.olszak@samsung.com>
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License
+*/
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief Inotify wrapper
+ */
+
+#include "utils/inotify.hpp"
+#include "utils/paths.hpp"
+#include "utils/fs.hpp"
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
+
+#include "logger/logger.hpp"
+
+#include <sys/ioctl.h>
+
+#include <functional>
+
+
+namespace utils {
+
+Inotify::Inotify(ipc::epoll::EventPoll& eventPoll)
+ :mEventPoll(eventPoll)
+{
+ mFD = ::inotify_init1(IN_CLOEXEC);
+ if (mFD == -1) {
+ const std::string msg = "Error in inotify_init1: " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+
+ mEventPoll.addFD(mFD, EPOLLIN, std::bind(&Inotify::handleInternal, this));
+}
+
+Inotify::~Inotify()
+{
+ Lock lock(mMutex);
+
+ for(const auto& handler: mHandlers) {
+ if (-1 == ::inotify_rm_watch(mFD, handler.watchID)) {
+ LOGE("Error in inotify_rm_watch: " + getSystemErrorMessage());
+ }
+ }
+ mEventPoll.removeFD(mFD);
+}
+
+int Inotify::getFD() const
+{
+ return mFD;
+}
+
+void Inotify::setHandler(const std::string& path,
+ const uint32_t eventMask,
+ const Callback&& callback)
+{
+ Lock lock(mMutex);
+
+ removeHandlerInternal(path);
+
+ int watchID = ::inotify_add_watch(mFD, path.c_str(), eventMask);
+ if (-1 == watchID) {
+ const std::string msg = "Error in inotify_add_watch: " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+
+ mHandlers.push_back({path, watchID, callback});
+}
+
+void Inotify::removeHandlerInternal(const std::string& path)
+{
+ // Find the corresponding handler's data
+ auto it = std::find_if(mHandlers.begin(), mHandlers.end(), [&path](const Handler& h) {
+ return path == h.path;
+ });
+
+ if (it == mHandlers.end()) {
+ return;
+ }
+
+ // Unwatch the path
+ if (-1 == ::inotify_rm_watch(mFD, it->watchID)) {
+ const std::string msg = "Error in inotify_rm_watch: " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+
+ mHandlers.erase(it);
+}
+
+void Inotify::removeHandler(const std::string& path)
+{
+ Lock lock(mMutex);
+ removeHandlerInternal(path);
+}
+
+void Inotify::handleInternal()
+{
+ Lock lock(mMutex);
+
+ // Get how much data is awaiting
+ unsigned int bufferSize;
+ utils::ioctl(mFD, FIONREAD, &bufferSize);
+
+ // Read all events into a buffer
+ std::vector<char> buffer(bufferSize);
+ utils::read(mFD, buffer.data(), bufferSize);
+
+ // Handle all events
+ unsigned int offset = 0;
+ while (offset < bufferSize) {
+ struct ::inotify_event *event = reinterpret_cast<struct ::inotify_event*>(&buffer[offset]);
+ offset = offset + sizeof(inotify_event) + event->len;
+
+ if(event->mask & IN_IGNORED) {
+ // Watch was removed - ignore
+ continue;
+ }
+
+ auto it = std::find_if(mHandlers.begin(), mHandlers.end(), [event](const Handler& h) {
+ return event->wd == h.watchID;
+ });
+ if (it == mHandlers.end()) {
+ // Meantime the callback was deleted by another callback
+ LOGE("No callback for file: " << event->name);
+ continue;
+ }
+
+ it->call(event->name, event->mask);
+ }
+}
+
+} // namespace utils
--- /dev/null
+/*
+* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Contact: Jan Olszak <j.olszak@samsung.com>
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License
+*/
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief Inotify wrapper
+ */
+
+#ifndef COMMON_UTILS_INOTIFY_HPP
+#define COMMON_UTILS_INOTIFY_HPP
+
+#include "ipc/epoll/event-poll.hpp"
+
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include <sys/inotify.h>
+
+
+namespace utils {
+
+/**
+ * Inotify monitors a directory and when a specified file or folder
+ * is created or deleted it calls a corresponding handler.
+ */
+class Inotify {
+public:
+ typedef std::function<void(const std::string&, const uint32_t)> Callback;
+
+ Inotify(ipc::epoll::EventPoll& eventPoll);
+ virtual ~Inotify();
+
+ Inotify(const Inotify&) = delete;
+ Inotify& operator=(const Inotify&) = delete;
+
+ /**
+ * Add a callback for a specified path
+ */
+ void setHandler(const std::string& path, const uint32_t eventMask, const Callback&& callback);
+
+ /**
+ * Stop watching the path
+ */
+ void removeHandler(const std::string& path);
+
+ /**
+ * @return inotify file descriptor
+ */
+ int getFD() const;
+
+private:
+ struct Handler {
+ std::string path;
+ int watchID;
+ Callback call;
+ };
+
+ typedef std::lock_guard<std::recursive_mutex> Lock;
+ std::recursive_mutex mMutex;
+
+ int mFD;
+ ipc::epoll::EventPoll& mEventPoll;
+ std::vector<Handler> mHandlers;
+
+ void handleInternal();
+ void removeHandlerInternal(const std::string& path);
+};
+
+} // namespace utils
+
+#endif // COMMON_UTILS_INOTIFY_HPP
}
}
-} // anonymous namespace
+} // namespace
/*
* Gets the dir name of a file path, analogous to dirname(1)
}
}
-
} // namespace utils
/**
* @file
* @author Jan Olszak (j.olszak@samsung.com)
- * @brief Eventfd wrapper
+ * @brief Signalfd wrapper
*/
#ifndef COMMON_UTILS_SIGNALFD_HPP
typedef std::function<void(const int sigNum)> Callback;
SignalFD(ipc::epoll::EventPoll& eventPoll);
- ~SignalFD();
+ virtual ~SignalFD();
SignalFD(const SignalFD& signalfd) = delete;
SignalFD& operator=(const SignalFD&) = delete;
--- /dev/null
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief Unit tests of Inotify
+ */
+
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "utils/inotify.hpp"
+#include "utils/fs.hpp"
+#include "utils/scoped-dir.hpp"
+#include "utils/value-latch.hpp"
+
+#include "logger/logger.hpp"
+
+#include "ipc/epoll/event-poll.hpp"
+#include "ipc/epoll/thread-dispatcher.hpp"
+
+#include <boost/filesystem.hpp>
+
+using namespace utils;
+namespace fs = boost::filesystem;
+
+namespace {
+
+const std::string TEST_DIR = "/tmp/ut-inotify/";
+const std::string DIR_NAME = "dir";
+const std::string FILE_NAME = "file.txt";
+
+const std::string DIR_PATH = TEST_DIR + DIR_NAME;
+const std::string FILE_PATH = TEST_DIR + FILE_NAME;
+
+
+struct Fixture {
+ utils::ScopedDir mTestDir;
+
+ Fixture()
+ :mTestDir(TEST_DIR)
+ {}
+};
+
+} // namespace
+
+
+BOOST_FIXTURE_TEST_SUITE(InotifySuite, Fixture)
+
+BOOST_AUTO_TEST_CASE(ConstructorDesctructor)
+{
+ ipc::epoll::EventPoll poll;
+ Inotify i(poll);
+}
+
+BOOST_AUTO_TEST_CASE(CreateDeleteFileHandler)
+{
+ ipc::epoll::ThreadDispatcher dispatcher;
+ Inotify i(dispatcher.getPoll());
+
+ // Callback on creation
+ ValueLatch<std::string> createResult;
+ i.setHandler(TEST_DIR, IN_CREATE, [&](const std::string& name, uint32_t) {
+ createResult.set(name);
+ });
+ utils::createFile(FILE_PATH, O_WRONLY | O_CREAT, 0666);
+ BOOST_REQUIRE_EQUAL(createResult.get(), FILE_NAME);
+
+ // Redefine the callback for delete
+ ValueLatch<std::string> deleteResult;
+ i.setHandler(TEST_DIR, IN_DELETE, [&](const std::string& name, uint32_t) {
+ deleteResult.set(name);
+ });
+ fs::remove(FILE_PATH);
+ BOOST_REQUIRE_EQUAL(deleteResult.get(), FILE_NAME);
+}
+
+BOOST_AUTO_TEST_CASE(CreateDeleteDirHandler)
+{
+ ipc::epoll::ThreadDispatcher dispatcher;
+ Inotify i(dispatcher.getPoll());
+
+ // Callback on creation
+ ValueLatch<std::string> createResult;
+ i.setHandler(TEST_DIR, IN_CREATE, [&](const std::string& name, uint32_t) {
+ createResult.set(name);
+ });
+ utils::createEmptyDir(DIR_PATH);
+ BOOST_REQUIRE_EQUAL(createResult.get(), DIR_NAME);
+
+
+ // Redefine the callback for delete
+ ValueLatch<std::string> deleteResult;
+ i.setHandler(TEST_DIR, IN_DELETE, [&](const std::string& name, uint32_t) {
+ deleteResult.set(name);
+ });
+ fs::remove_all(DIR_PATH);
+ BOOST_REQUIRE_EQUAL(deleteResult.get(), DIR_NAME);
+}
+
+BOOST_AUTO_TEST_CASE(NoFalseEventHandler)
+{
+ ipc::epoll::ThreadDispatcher dispatcher;
+ Inotify i(dispatcher.getPoll());
+
+ utils::createFile(FILE_PATH, O_WRONLY | O_CREAT, 0666);
+
+ // Callback on creation shouldn't be called
+ ValueLatch<std::string> createResult;
+ i.setHandler(TEST_DIR, IN_CREATE, [&](const std::string& name, uint32_t) {
+ createResult.set(name);
+ });
+ fs::remove(FILE_PATH);
+ BOOST_REQUIRE_THROW(createResult.get(10), UtilsException);
+}
+
+BOOST_AUTO_TEST_CASE(RemoveHandler)
+{
+ ipc::epoll::ThreadDispatcher dispatcher;
+ Inotify i(dispatcher.getPoll());
+
+ // Callback on creation
+ ValueLatch<std::string> createResult;
+ i.setHandler(TEST_DIR, IN_CREATE, [&](const std::string& name, uint32_t) {
+ createResult.set(name);
+ });
+ i.removeHandler(TEST_DIR);
+ utils::createFile(FILE_PATH, O_WRONLY | O_CREAT, 0666);
+ fs::remove(FILE_PATH);
+ BOOST_REQUIRE_THROW(createResult.get(10), UtilsException);
+}
+
+BOOST_AUTO_TEST_SUITE_END()