| Gunnar Mills | 6bd6d7b | 2017-09-18 09:22:36 -0500 | [diff] [blame] | 1 | #include <stdexcept> | 
 | 2 | #include <cstddef> | 
 | 3 | #include <cstring> | 
 | 4 | #include <string> | 
 | 5 | #include <sys/inotify.h> | 
 | 6 | #include <unistd.h> | 
 | 7 | #include <experimental/filesystem> | 
 | 8 | #include <phosphor-logging/log.hpp> | 
 | 9 | #include "config.h" | 
 | 10 | #include "watch.hpp" | 
 | 11 |  | 
 | 12 | namespace openpower | 
 | 13 | { | 
 | 14 | namespace software | 
 | 15 | { | 
 | 16 | namespace updater | 
 | 17 | { | 
 | 18 |  | 
 | 19 | using namespace phosphor::logging; | 
 | 20 | namespace fs = std::experimental::filesystem; | 
 | 21 |  | 
 | 22 | Watch::Watch(sd_event* loop, | 
 | 23 |              std::function<void(std::string&)> functionalCallback) : | 
 | 24 |         functionalCallback(functionalCallback), | 
 | 25 |         fd(inotifyInit()) | 
 | 26 |  | 
 | 27 | { | 
 | 28 |     // Create PNOR_ACTIVE_PATH if doesn't exist. | 
 | 29 |     if (!fs::is_directory(PNOR_ACTIVE_PATH)) | 
 | 30 |     { | 
 | 31 |         fs::create_directories(PNOR_ACTIVE_PATH); | 
 | 32 |     } | 
 | 33 |  | 
 | 34 |     wd = inotify_add_watch(fd(), PNOR_ACTIVE_PATH, IN_CREATE); | 
 | 35 |     if (-1 == wd) | 
 | 36 |     { | 
 | 37 |         auto error = errno; | 
 | 38 |         throw std::system_error(error, | 
 | 39 |                                 std::generic_category(), | 
 | 40 |                                 "Error occurred during the inotify_init1"); | 
 | 41 |     } | 
 | 42 |  | 
 | 43 |     decltype(eventSource.get()) sourcePtr = nullptr; | 
 | 44 |     auto rc = sd_event_add_io(loop, | 
 | 45 |                               &sourcePtr, | 
 | 46 |                               fd(), | 
 | 47 |                               EPOLLIN, | 
 | 48 |                               callback, | 
 | 49 |                               this); | 
 | 50 |  | 
 | 51 |     eventSource.reset(sourcePtr); | 
 | 52 |  | 
 | 53 |     if (0 > rc) | 
 | 54 |     { | 
 | 55 |         throw std::system_error(-rc, | 
 | 56 |                                 std::generic_category(), | 
 | 57 |                                 "Error occurred during the inotify_init1"); | 
 | 58 |     } | 
 | 59 | } | 
 | 60 |  | 
 | 61 | Watch::~Watch() | 
 | 62 | { | 
 | 63 |     if ((-1 != fd()) && (-1 != wd)) | 
 | 64 |     { | 
 | 65 |         inotify_rm_watch(fd(), wd); | 
 | 66 |     } | 
 | 67 | } | 
 | 68 |  | 
 | 69 | int Watch::callback(sd_event_source* s, | 
 | 70 |                     int fd, | 
 | 71 |                     uint32_t revents, | 
 | 72 |                     void* userdata) | 
 | 73 | { | 
 | 74 |     if (!(revents & EPOLLIN)) | 
 | 75 |     { | 
 | 76 |         return 0; | 
 | 77 |     } | 
 | 78 |  | 
 | 79 |     constexpr auto maxBytes = 1024; | 
 | 80 |     uint8_t buffer[maxBytes]; | 
 | 81 |     auto bytes = read(fd, buffer, maxBytes); | 
 | 82 |     if (0 > bytes) | 
 | 83 |     { | 
 | 84 |         auto error = errno; | 
 | 85 |         throw std::system_error(error, | 
 | 86 |                                 std::generic_category(), | 
 | 87 |                                 "failed to read inotify event"); | 
 | 88 |     } | 
 | 89 |  | 
 | 90 |     auto offset = 0; | 
 | 91 |     while (offset < bytes) | 
 | 92 |     { | 
 | 93 |         auto event = reinterpret_cast<inotify_event*>(&buffer[offset]); | 
 | 94 |         // Update the functional association on a RO | 
 | 95 |         // active image symlink change | 
 | 96 |         fs::path path(PNOR_ACTIVE_PATH); | 
 | 97 |         path /= event->name; | 
 | 98 |         if (fs::equivalent(path, PNOR_RO_ACTIVE_PATH)) | 
 | 99 |         { | 
 | 100 |             auto target = fs::canonical(path).string(); | 
 | 101 |  | 
 | 102 |             // Get the image <id> from the symlink target | 
 | 103 |             // for example /media/ro-2a1022fe | 
 | 104 |             static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX); | 
 | 105 |             auto id = target.substr(PNOR_RO_PREFIX_LEN); | 
 | 106 |             auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; | 
 | 107 |  | 
 | 108 |             static_cast<Watch*>(userdata)->functionalCallback(objPath); | 
 | 109 |         } | 
 | 110 |         offset += offsetof(inotify_event, name) + event->len; | 
 | 111 |     } | 
 | 112 |  | 
 | 113 |     return 0; | 
 | 114 | } | 
 | 115 |  | 
 | 116 | int Watch::inotifyInit() | 
 | 117 | { | 
 | 118 |     auto fd = inotify_init1(IN_NONBLOCK); | 
 | 119 |  | 
 | 120 |     if (-1 == fd) | 
 | 121 |     { | 
 | 122 |         auto error = errno; | 
 | 123 |         throw std::system_error(error, | 
 | 124 |                                 std::generic_category(), | 
 | 125 |                                 "Error occurred during the inotify_init1"); | 
 | 126 |     } | 
 | 127 |  | 
 | 128 |     return fd; | 
 | 129 | } | 
 | 130 |  | 
 | 131 | } // namespace updater | 
 | 132 | } // namespace software | 
 | 133 | } // namespace openpower |