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