| #include <stdexcept> |
| #include <cstddef> |
| #include <cstring> |
| #include <string> |
| #include <sys/inotify.h> |
| #include <unistd.h> |
| #include <experimental/filesystem> |
| #include <phosphor-logging/log.hpp> |
| #include "config.h" |
| #include "watch.hpp" |
| |
| namespace openpower |
| { |
| namespace software |
| { |
| namespace updater |
| { |
| |
| using namespace phosphor::logging; |
| namespace fs = std::experimental::filesystem; |
| |
| Watch::Watch(sd_event* loop, |
| std::function<void(std::string&)> functionalCallback) : |
| functionalCallback(functionalCallback), |
| fd(inotifyInit()) |
| |
| { |
| // Create PNOR_ACTIVE_PATH if doesn't exist. |
| if (!fs::is_directory(PNOR_ACTIVE_PATH)) |
| { |
| fs::create_directories(PNOR_ACTIVE_PATH); |
| } |
| |
| wd = inotify_add_watch(fd(), PNOR_ACTIVE_PATH, IN_CREATE); |
| if (-1 == wd) |
| { |
| auto error = errno; |
| throw std::system_error(error, |
| std::generic_category(), |
| "Error occurred during the inotify_init1"); |
| } |
| |
| decltype(eventSource.get()) sourcePtr = nullptr; |
| auto rc = sd_event_add_io(loop, |
| &sourcePtr, |
| fd(), |
| EPOLLIN, |
| callback, |
| this); |
| |
| eventSource.reset(sourcePtr); |
| |
| if (0 > rc) |
| { |
| throw std::system_error(-rc, |
| std::generic_category(), |
| "Error occurred during the inotify_init1"); |
| } |
| } |
| |
| Watch::~Watch() |
| { |
| if ((-1 != fd()) && (-1 != wd)) |
| { |
| inotify_rm_watch(fd(), wd); |
| } |
| } |
| |
| int Watch::callback(sd_event_source* s, |
| int fd, |
| uint32_t revents, |
| void* userdata) |
| { |
| if (!(revents & EPOLLIN)) |
| { |
| return 0; |
| } |
| |
| constexpr auto maxBytes = 1024; |
| uint8_t buffer[maxBytes]; |
| auto bytes = read(fd, buffer, maxBytes); |
| if (0 > bytes) |
| { |
| auto error = errno; |
| throw std::system_error(error, |
| std::generic_category(), |
| "failed to read inotify event"); |
| } |
| |
| auto offset = 0; |
| while (offset < bytes) |
| { |
| auto event = reinterpret_cast<inotify_event*>(&buffer[offset]); |
| // Update the functional association on a RO |
| // active image symlink change |
| fs::path path(PNOR_ACTIVE_PATH); |
| path /= event->name; |
| if (fs::equivalent(path, PNOR_RO_ACTIVE_PATH)) |
| { |
| auto target = fs::canonical(path).string(); |
| |
| // Get the image <id> from the symlink target |
| // for example /media/ro-2a1022fe |
| static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX); |
| auto id = target.substr(PNOR_RO_PREFIX_LEN); |
| auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; |
| |
| static_cast<Watch*>(userdata)->functionalCallback(objPath); |
| } |
| offset += offsetof(inotify_event, name) + event->len; |
| } |
| |
| return 0; |
| } |
| |
| int Watch::inotifyInit() |
| { |
| auto fd = inotify_init1(IN_NONBLOCK); |
| |
| if (-1 == fd) |
| { |
| auto error = errno; |
| throw std::system_error(error, |
| std::generic_category(), |
| "Error occurred during the inotify_init1"); |
| } |
| |
| return fd; |
| } |
| |
| } // namespace updater |
| } // namespace software |
| } // namespace openpower |