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