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