1 #include "watch.hpp" 2 3 #include <sys/inotify.h> 4 #include <unistd.h> 5 6 #include <cstddef> 7 #include <cstring> 8 #include <filesystem> 9 #include <stdexcept> 10 #include <string> 11 12 namespace pldm 13 { 14 15 namespace fw_update 16 { 17 18 // using namespace phosphor::logging; 19 using namespace std::string_literals; 20 namespace fs = std::filesystem; 21 22 Watch::Watch(sd_event* loop, std::function<int(std::string&)> imageCallback) : 23 imageCallback(imageCallback) 24 { 25 // Check if IMAGE DIR exists. 26 fs::path imgDirPath("/tmp/images"); 27 if (!fs::is_directory(imgDirPath)) 28 { 29 fs::create_directories(imgDirPath); 30 } 31 32 fd = inotify_init1(IN_NONBLOCK); 33 if (-1 == fd) 34 { 35 // Store a copy of errno, because the string creation below will 36 // invalidate errno due to one more system calls. 37 auto error = errno; 38 throw std::runtime_error( 39 "inotify_init1 failed, errno="s + std::strerror(error)); 40 } 41 42 wd = inotify_add_watch(fd, "/tmp/images", IN_CLOSE_WRITE); 43 if (-1 == wd) 44 { 45 auto error = errno; 46 close(fd); 47 throw std::runtime_error( 48 "inotify_add_watch failed, errno="s + std::strerror(error)); 49 } 50 51 auto rc = sd_event_add_io(loop, nullptr, fd, EPOLLIN, callback, this); 52 if (0 > rc) 53 { 54 throw std::runtime_error( 55 "failed to add to event loop, rc="s + std::strerror(-rc)); 56 } 57 } 58 59 Watch::~Watch() 60 { 61 if (-1 != fd) 62 { 63 if (-1 != wd) 64 { 65 inotify_rm_watch(fd, wd); 66 } 67 close(fd); 68 } 69 } 70 71 int Watch::callback(sd_event_source* /* s */, int fd, 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::runtime_error( 86 "failed to read inotify event, errno="s + std::strerror(error)); 87 } 88 89 auto offset = 0; 90 while (offset < bytes) 91 { 92 auto event = reinterpret_cast<inotify_event*>(&buffer[offset]); 93 if ((event->mask & IN_CLOSE_WRITE) && !(event->mask & IN_ISDIR)) 94 { 95 auto tarballPath = std::string{"/tmp/images"} + '/' + event->name; 96 auto rc = static_cast<Watch*>(userdata)->imageCallback(tarballPath); 97 if (rc < 0) 98 { 99 // log<level::ERR>("Error processing image", 100 // entry("IMAGE=%s", tarballPath.c_str())); 101 } 102 } 103 104 offset += offsetof(inotify_event, name) + event->len; 105 } 106 107 return 0; 108 } 109 110 } // namespace fw_update 111 } // namespace pldm 112