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 (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, 72 [this](sdeventplus::source::IO&, int fd, uint32_t revents) { 73 const int size = sizeof(struct inotify_event) + NAME_MAX + 1; 74 std::array<char, size> buffer; 75 int length = read(fd, buffer.data(), buffer.size()); 76 if (length >= static_cast<int>(sizeof(struct inotify_event))) 77 { 78 struct inotify_event* notifyEvent = 79 reinterpret_cast<struct inotify_event*>(&buffer[0]); 80 if (notifyEvent->len) 81 { 82 if (watchFile == notifyEvent->name) 83 { 84 callback(); 85 } 86 } 87 } 88 else 89 { 90 log<level::ERR>("Failed to read inotify event"); 91 } 92 }); 93 } 94 95 void Watch::stopWatch() 96 { 97 if (-1 != fd) 98 { 99 if (-1 != wd) 100 { 101 inotify_rm_watch(fd, wd); 102 } 103 close(fd); 104 } 105 if (ioPtr) 106 { 107 ioPtr.reset(); 108 } 109 } 110 111 } // namespace certs 112 } // namespace phosphor 113