xref: /openbmc/phosphor-certificate-manager/watch.cpp (revision 223e460421eebb1c598d9285b0cb01f1150fa50d)
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