1 #include "watch.hpp" 2 3 #include <sys/epoll.h> 4 #include <sys/inotify.h> 5 #include <unistd.h> 6 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/elog.hpp> 9 #include <phosphor-logging/lg2.hpp> 10 #include <sdeventplus/source/io.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 13 #include <array> 14 #include <cerrno> 15 #include <climits> 16 #include <cstdint> 17 #include <cstring> 18 #include <filesystem> 19 20 namespace phosphor::certs 21 { 22 23 using ::phosphor::logging::elog; 24 using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 25 namespace fs = std::filesystem; 26 27 Watch::Watch(sdeventplus::Event& event, std::string& certFile, Callback cb) : 28 event(event), callback(std::move(cb)) 29 { 30 // get parent directory of certificate file to watch 31 fs::path path = fs::path(certFile).parent_path(); 32 try 33 { 34 if (!fs::exists(path)) 35 { 36 fs::create_directories(path); 37 } 38 } 39 catch (const fs::filesystem_error& e) 40 { 41 lg2::error( 42 "Failed to create directory, ERR:{ERR}, DIRECTORY:{DIRECTORY}", 43 "ERR", e, "DIRECTORY", path); 44 elog<InternalFailure>(); 45 } 46 watchDir = path; 47 watchFile = fs::path(certFile).filename(); 48 startWatch(); 49 } 50 51 Watch::~Watch() 52 { 53 stopWatch(); 54 } 55 56 void Watch::startWatch() 57 { 58 // stop any existing watch 59 stopWatch(); 60 61 fd = inotify_init1(IN_NONBLOCK); 62 if (-1 == fd) 63 { 64 lg2::error("inotify_init1 failed: {ERR}", "ERR", std::strerror(errno)); 65 elog<InternalFailure>(); 66 } 67 wd = inotify_add_watch(fd, watchDir.c_str(), IN_CLOSE_WRITE); 68 if (-1 == wd) 69 { 70 close(fd); 71 lg2::error("inotify_add_watch failed, ERR:{ERR}, WATCH:{WATCH}", "ERR", 72 std::strerror(errno), "WATCH", watchDir); 73 elog<InternalFailure>(); 74 } 75 76 ioPtr = std::make_unique<sdeventplus::source::IO>( 77 event, fd, EPOLLIN, [this](sdeventplus::source::IO&, int fd, uint32_t) { 78 constexpr int size = sizeof(struct inotify_event) + NAME_MAX + 1; 79 std::array<char, size> buffer{}; 80 int length = read(fd, buffer.data(), buffer.size()); 81 if (length >= static_cast<int>(sizeof(struct inotify_event))) 82 { 83 struct inotify_event* notifyEvent = 84 reinterpret_cast<struct inotify_event*>(&buffer[0]); 85 if (notifyEvent->len) 86 { 87 if (watchFile == notifyEvent->name) 88 { 89 callback(); 90 } 91 } 92 } 93 else 94 { 95 lg2::error("Failed to read inotify event"); 96 } 97 }); 98 } 99 100 void Watch::stopWatch() 101 { 102 if (-1 != fd) 103 { 104 if (-1 != wd) 105 { 106 inotify_rm_watch(fd, wd); 107 } 108 close(fd); 109 } 110 if (ioPtr) 111 { 112 ioPtr.reset(); 113 } 114 } 115 116 } // namespace phosphor::certs 117