osaf/libs/core/cplusplus/base/Makefile.am | 2 + osaf/libs/core/cplusplus/base/file_notify.cc | 175 +++++++++++++++++++++++++++ osaf/libs/core/cplusplus/base/file_notify.h | 87 +++++++++++++ 3 files changed, 264 insertions(+), 0 deletions(-)
diff --git a/osaf/libs/core/cplusplus/base/Makefile.am b/osaf/libs/core/cplusplus/base/Makefile.am --- a/osaf/libs/core/cplusplus/base/Makefile.am +++ b/osaf/libs/core/cplusplus/base/Makefile.am @@ -23,6 +23,7 @@ DEFAULT_INCLUDES = SUBDIRS = tests noinst_HEADERS = \ + file_notify.h \ getenv.h \ macros.h \ process.h \ @@ -38,5 +39,6 @@ libbase_la_CPPFLAGS = \ libbase_la_LDFLAGS = -static libbase_la_SOURCES = \ + file_notify.cc \ getenv.cc \ process.cc diff --git a/osaf/libs/core/cplusplus/base/file_notify.cc b/osaf/libs/core/cplusplus/base/file_notify.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/file_notify.cc @@ -0,0 +1,175 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include "base/file_notify.h" +#include <libgen.h> +#include <cstring> +#include "time.h" +#include "osaf_poll.h" +#include "logtrace.h" + +namespace base { + +FileNotify::FileNotify() { + if ((inotify_fd_ = inotify_init()) == -1) { + LOG_NO("inotify_init failed: %s", strerror(errno)); + } +} + +FileNotify::~FileNotify() { + close(inotify_fd_); +} + +void FileNotify::SplitFileName(const std::string &file_name) { + char *tmp1 = strdup(file_name.c_str()); + char *tmp2 = strdup(file_name.c_str()); + file_path_ = dirname(tmp1); + file_name_ = basename(tmp2); + free(tmp1); + free(tmp2); +} + +FileNotify::FileNotifyErrors +FileNotify::WaitForFileCreation(const std::string &file_name, + int timeout, int user_fd) { + FileNotify::FileNotifyErrors rc {FileNotifyErrors::kOK}; + SplitFileName(file_name); + + if ((inotify_wd_ = + inotify_add_watch(inotify_fd_, file_path_.c_str(), IN_CREATE)) == -1) { + LOG_NO("inotify_add_watch failed: %s", strerror(errno)); + return FileNotifyErrors::kError; + } + + if (FileExists(file_name)) { + TRACE("File already created: %s", file_name.c_str()); + inotify_rm_watch(inotify_fd_, inotify_wd_); + return FileNotifyErrors::kOK; + } + + rc = ProcessEvents(timeout, user_fd); + inotify_rm_watch(inotify_fd_, inotify_wd_); + return rc; +} + +FileNotify::FileNotifyErrors +FileNotify::WaitForFileDeletion( + const std::string &file_name, int timeout, int user_fd) { + FileNotify::FileNotifyErrors rc { FileNotifyErrors::kOK }; + + if ((inotify_wd_ = inotify_add_watch(inotify_fd_, file_name.c_str(), + IN_DELETE_SELF)) == -1) { + if (errno == ENOENT) { + TRACE("File already deleted: %s", file_name.c_str()); + return FileNotifyErrors::kOK; + } else { + LOG_NO("inotify_add_watch failed: %s", strerror(errno)); + return FileNotifyErrors::kError; + } + } + + rc = ProcessEvents(timeout, user_fd); + inotify_rm_watch(inotify_fd_, inotify_wd_); + return rc; +} + +FileNotify::FileNotifyErrors +FileNotify::ProcessEvents(int timeout, int user_fd) { + enum { + FD_INOTIFY = 0, + FD_USER, + NUM_FDS + }; + + timespec start_time; + timespec time_left_ts; + timespec timeout_ts; + pollfd fds[NUM_FDS] {}; + fds[FD_INOTIFY].fd = inotify_fd_; + fds[FD_INOTIFY].events = POLLIN; + fds[FD_USER].fd = user_fd; + fds[FD_USER].events = POLLIN; + + osaf_millis_to_timespec(timeout, &timeout_ts); + start_time = base::ReadMonotonicClock(); + + while (true) { + timespec current_time {0}; + timespec elapsed_time {0}; + + TRACE("remaining timeout: %d", timeout); + int rc = osaf_poll(&fds[0], NUM_FDS, timeout); + + if (rc > 0) { + if (fds[FD_INOTIFY].revents & POLLIN) { + ssize_t num_read = read(inotify_fd_, buf_, kBufferSize); + + if (num_read == 0) { + LOG_WA("read returned zero"); + return FileNotifyErrors::kError; + } else if (num_read == -1) { + if (errno == EINTR) { + continue; + } else { + LOG_WA("read error: %s", strerror(errno)); + return FileNotifyErrors::kError; + } + } else { + for (char *p = buf_; p < buf_ + num_read;) { + inotify_event *event = reinterpret_cast<inotify_event*> (p); + if (event->mask & IN_DELETE_SELF) { + TRACE("file name: %s deleted", file_name_.c_str()); + return FileNotifyErrors::kOK; + } + if (event->mask & IN_CREATE) { + if (file_name_ == event->name) { + TRACE("file name: %s created", file_name_.c_str()); + return FileNotifyErrors::kOK; + } + } + if (event->mask & IN_IGNORED) { + TRACE("IN_IGNORE received, (ignored)"); + } + p += sizeof(inotify_event) + event->len; + } + // calculate remaining timeout + current_time = base::ReadMonotonicClock(); + + if (current_time >= start_time) { + elapsed_time = current_time - start_time; + } + if (elapsed_time >= timeout_ts) { + return FileNotifyErrors::kTimeOut; + } + time_left_ts = timeout_ts - elapsed_time; + + timeout = base::TimespecToMillis(time_left_ts); + } + } + if (fds[FD_USER].revents & POLLIN) { + return FileNotifyErrors::kUserFD; + } + } else if (rc == 0) { + TRACE("timeout"); + return FileNotifyErrors::kTimeOut; + } else { + LOG_WA("poll error %s", strerror(errno)); + return FileNotifyErrors::kError; + } + } +} +} // namespace base diff --git a/osaf/libs/core/cplusplus/base/file_notify.h b/osaf/libs/core/cplusplus/base/file_notify.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/file_notify.h @@ -0,0 +1,87 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include <unistd.h> +#include <sys/stat.h> +#include <limits.h> +#include <sys/inotify.h> +#include <string> +#include "base/macros.h" + +#ifndef OSAF_LIBS_CORE_CPLUSPLUS_BASE_FILE_NOTIFY_H_ +#define OSAF_LIBS_CORE_CPLUSPLUS_BASE_FILE_NOTIFY_H_ + +namespace base { + +class FileNotify { + public: + enum class FileNotifyErrors { + kOK = 0, + kTimeOut, + kError, + kUserFD + }; + + FileNotify(); + virtual ~FileNotify(); + + /** + * @brief Wait for a file to be created with a timeout. + * + * @a file name in format /path/file. + * This function blocks until the file has been created or until the @a timeout expires, + * whichever happens first. + * + */ + FileNotifyErrors WaitForFileCreation(const std::string &file_name, + int timeout, int user_fd = -1); + + /** + * @brief Wait for a file to be deleted. + * + * @a file name in format /path/file. + * This function blocks until the file has been deleted or until the @a timeout expires, + * whichever happens first. + * + */ + FileNotifyErrors WaitForFileDeletion(const std::string &file_name, + int timeout, int user_fd = -1); + + private: + static const int kBufferSize = 10 * (sizeof (inotify_event) + NAME_MAX + 1); + + FileNotifyErrors ProcessEvents(int timeout, int user_fd); + + bool FileExists(const std::string& file_name) const { + struct stat buffer; + return stat(file_name.c_str(), &buffer) == 0; + } + + void SplitFileName(const std::string &file_name); + + char buf_[kBufferSize] __attribute__((aligned(8))) = {}; + std::string file_path_; + std::string file_name_; + + int inotify_fd_{-1}; + int inotify_wd_{-1}; + DELETE_COPY_AND_MOVE_OPERATORS(FileNotify); +}; + +} // namespace base + +#endif // OSAF_LIBS_CORE_CPLUSPLUS_BASE_FILE_NOTIFY_H_ ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, SlashDot.org! http://sdm.link/slashdot _______________________________________________ Opensaf-devel mailing list Opensaf-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/opensaf-devel