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