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