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