1 #include "config.h"
2 
3 #include "sync_watch.hpp"
4 
5 #include <sys/inotify.h>
6 #include <unistd.h>
7 
8 #include <phosphor-logging/lg2.hpp>
9 
10 #include <filesystem>
11 #include <fstream>
12 #include <system_error>
13 
14 namespace phosphor
15 {
16 namespace software
17 {
18 namespace manager
19 {
20 
21 PHOSPHOR_LOG2_USING;
22 
23 void SyncWatch::addInotifyWatch(const fs::path& path)
24 {
25     auto wd =
26         inotify_add_watch(inotifyFd, path.c_str(), IN_CLOSE_WRITE | IN_DELETE);
27     if (-1 == wd)
28     {
29         error("inotify_add_watch on {PATH} failed: {ERRNO}", "ERRNO", errno,
30               "PATH", path);
31         return;
32     }
33 
34     fileMap[wd] = fs::path(path);
35 }
36 
37 SyncWatch::SyncWatch(sd_event& loop,
38                      std::function<int(int, fs::path&)> syncCallback) :
39     inotifyFd(-1), syncCallback(std::move(syncCallback)), loop(loop)
40 {
41     auto fd = inotify_init1(IN_NONBLOCK);
42     if (-1 == fd)
43     {
44         error("inotify_init1 failed: {ERRNO}", "ERRNO", errno);
45         return;
46     }
47     inotifyFd = fd;
48 
49     auto rc = sd_event_add_io(&loop, nullptr, fd, EPOLLIN, callback, this);
50     if (0 > rc)
51     {
52         error("failed to add to event loop: {RC}", "RC", rc);
53         return;
54     }
55 
56     std::error_code ec;
57     auto syncfile = fs::path(SYNC_LIST_DIR_PATH) / SYNC_LIST_FILE_NAME;
58     if (fs::exists(syncfile, ec))
59     {
60         std::string line;
61         std::ifstream file(syncfile.c_str());
62         while (std::getline(file, line))
63         {
64             addInotifyWatch(line);
65         }
66     }
67 }
68 
69 SyncWatch::~SyncWatch()
70 {
71     if (inotifyFd != -1)
72     {
73         close(inotifyFd);
74     }
75 }
76 
77 int SyncWatch::callback(sd_event_source* /* s */, int fd, uint32_t revents,
78                         void* userdata)
79 {
80     if (!(revents & EPOLLIN))
81     {
82         return 0;
83     }
84 
85     constexpr auto maxBytes = 1024;
86     uint8_t buffer[maxBytes];
87     auto bytes = read(fd, buffer, maxBytes);
88     if (0 > bytes)
89     {
90         return 0;
91     }
92 
93     auto syncWatch = static_cast<SyncWatch*>(userdata);
94     auto offset = 0;
95     while (offset < bytes)
96     {
97         auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
98 
99         // Watch was removed, re-add it if file still exists.
100         if (event->mask & IN_IGNORED)
101         {
102             std::error_code ec;
103             if (fs::exists(syncWatch->fileMap[event->wd], ec))
104             {
105                 syncWatch->addInotifyWatch(syncWatch->fileMap[event->wd]);
106             }
107             else
108             {
109                 info("The inotify watch on {PATH} was removed", "PATH",
110                      syncWatch->fileMap[event->wd]);
111             }
112             return 0;
113         }
114 
115         // fileMap<wd, path>
116         auto rc = syncWatch->syncCallback(static_cast<int>(event->mask),
117                                           syncWatch->fileMap[event->wd]);
118         if (rc)
119         {
120             return rc;
121         }
122 
123         offset += offsetof(inotify_event, name) + event->len;
124     }
125 
126     return 0;
127 }
128 
129 } // namespace manager
130 } // namespace software
131 } // namespace phosphor
132