diff options
author | Gunnar Mills <gmills@us.ibm.com> | 2017-09-18 09:22:36 -0500 |
---|---|---|
committer | Brad Bishop <bradleyb@fuzziesquirrel.com> | 2017-09-29 03:58:25 +0000 |
commit | 6bd6d7bbc8d66c2ba74ab20821ef751046243f42 (patch) | |
tree | 14324f3540749f5456c71585e167b84712c0acef | |
parent | 234a07e856d3d03fe474a13d335df4d32e316ce6 (diff) | |
download | openpower-pnor-code-mgmt-6bd6d7bbc8d66c2ba74ab20821ef751046243f42.tar.gz openpower-pnor-code-mgmt-6bd6d7bbc8d66c2ba74ab20821ef751046243f42.zip |
Call updateFunctionalAssociation on symlink change
Call updateFunctionalAssociation, to change the functional
association to the new "running" PNOR image, on a symlink change.
The symlink change occurs on chassis poweron.
Look at the RO symlink, /var/lib/phosphor-software-manager/pnor/ro,
for a symlink change.
Change-Id: I2c673635af55da1c642e6d96ab6e12951b3a4fd3
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
-rwxr-xr-x | Makefile.am | 1 | ||||
-rwxr-xr-x | item_updater_main.cpp | 43 | ||||
-rw-r--r-- | watch.cpp | 133 | ||||
-rw-r--r-- | watch.hpp | 123 |
4 files changed, 295 insertions, 5 deletions
diff --git a/Makefile.am b/Makefile.am index d9880fba7..0b94db409 100755 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,7 @@ openpower_update_manager_SOURCES = \ activation.cpp \ version.cpp \ serialize.cpp \ + watch.cpp \ item_updater.cpp \ item_updater_main.cpp diff --git a/item_updater_main.cpp b/item_updater_main.cpp index c6f7dbad2..0933505ae 100755 --- a/item_updater_main.cpp +++ b/item_updater_main.cpp @@ -1,23 +1,56 @@ #include <sdbusplus/bus.hpp> #include <sdbusplus/server/manager.hpp> +#include <system_error> #include "config.h" #include "item_updater.hpp" +#include <phosphor-logging/log.hpp> +#include "watch.hpp" int main(int argc, char* argv[]) { + using namespace openpower::software::updater; + using namespace phosphor::logging; auto bus = sdbusplus::bus::new_default(); + sd_event* loop = nullptr; + auto rc = sd_event_default(&loop); + if (rc < 0) + { + log<level::ERR>("Error occurred during the sd_event_default", + entry("RC=%d", rc)); + return -1; + } + // Add sdbusplus ObjectManager. sdbusplus::server::manager::manager objManager(bus, SOFTWARE_OBJPATH); - openpower::software::updater::ItemUpdater updater(bus, SOFTWARE_OBJPATH); + ItemUpdater updater(bus, SOFTWARE_OBJPATH); bus.request_name(BUSNAME_UPDATER); - - while (true) + try { - bus.process_discard(); - bus.wait(); + openpower::software::updater::Watch watch( + loop, + std::bind(std::mem_fn( + &ItemUpdater::updateFunctionalAssociation), + &updater, + std::placeholders::_1)); + bus.attach_event(loop, SD_EVENT_PRIORITY_NORMAL); + auto rc = sd_event_loop(loop); + if (rc < 0) + { + log<level::ERR>("Error occurred during the sd_event_loop", + entry("RC=%d", rc)); + return -1; + } } + catch (const std::system_error& e) + { + log<level::ERR>(e.what()); + return -1; + } + + sd_event_unref(loop); + return 0; } diff --git a/watch.cpp b/watch.cpp new file mode 100644 index 000000000..da06bbc1e --- /dev/null +++ b/watch.cpp @@ -0,0 +1,133 @@ +#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 diff --git a/watch.hpp b/watch.hpp new file mode 100644 index 000000000..bdea792c0 --- /dev/null +++ b/watch.hpp @@ -0,0 +1,123 @@ +#pragma once + +#include <systemd/sd-event.h> +#include <unistd.h> + +namespace openpower +{ +namespace software +{ +namespace updater +{ + +/* Need a custom deleter for freeing up sd_event_source */ +struct EventSourceDeleter +{ + void operator()(sd_event_source* eventSource) const + { + eventSource = sd_event_source_unref(eventSource); + } +}; +using EventSourcePtr = std::unique_ptr<sd_event_source, EventSourceDeleter>; + +/** @struct CustomFd + * + * RAII wrapper for file descriptor. + */ +struct CustomFd +{ + public: + CustomFd() = delete; + CustomFd(const CustomFd&) = delete; + CustomFd& operator=(const CustomFd&) = delete; + CustomFd(CustomFd&&) = delete; + CustomFd& operator=(CustomFd&&) = delete; + + /** @brief Saves File descriptor and uses it to do file operation + * + * @param[in] fd - File descriptor + */ + CustomFd(int fd) : fd(fd) {} + + ~CustomFd() + { + if (fd >= 0) + { + close(fd); + } + } + + int operator()() const + { + return fd; + } + + private: + /** @brief File descriptor */ + int fd = -1; +}; + +/** @class Watch + * + * @brief Adds inotify watch on PNOR symlinks file to monitor for changes in + * "running" PNOR version + * + * The inotify watch is hooked up with sd-event, so that on call back, + * appropriate actions related to a change in the "running" PNOR version + * can be taken. + */ +class Watch +{ + public: + /** @brief ctor - hook inotify watch with sd-event + * + * @param[in] loop - sd-event object + * @param[in] functionalCallback - The callback function for updating + * the functional associations. + */ + Watch(sd_event* loop, + std::function<void(std::string&)> functionalCallback); + + Watch(const Watch&) = delete; + Watch& operator=(const Watch&) = delete; + Watch(Watch&&) = delete; + Watch& operator=(Watch&&) = delete; + + /** @brief dtor - remove inotify watch + */ + ~Watch(); + + private: + /** @brief sd-event callback + * + * @param[in] s - event source, floating (unused) in our case + * @param[in] fd - inotify fd + * @param[in] revents - events that matched for fd + * @param[in] userdata - pointer to Watch object + * @returns 0 on success, -1 on fail + */ + static int callback(sd_event_source* s, + int fd, + uint32_t revents, + void* userdata); + + /** initialize an inotify instance and returns file descriptor */ + int inotifyInit(); + + /** @brief PNOR symlink file watch descriptor */ + int wd = -1; + + /** @brief event source */ + EventSourcePtr eventSource; + + /** @brief The callback function for updating the + functional associations. */ + std::function<void(std::string&)> functionalCallback; + + /** @brief inotify file descriptor */ + CustomFd fd; +}; + +} // namespace updater +} // namespace software +} // namespace openpower |