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