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