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