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 
13 namespace phosphor
14 {
15 namespace software
16 {
17 namespace manager
18 {
19 
20 PHOSPHOR_LOG2_USING;
21 
22 void SyncWatch::addInotifyWatch(const fs::path& path)
23 {
24     auto wd =
25         inotify_add_watch(inotifyFd, path.c_str(), IN_CLOSE_WRITE | IN_DELETE);
26     if (-1 == wd)
27     {
28         error("inotify_add_watch on {PATH} failed: {ERRNO}", "ERRNO", errno,
29               "PATH", path);
30         return;
31     }
32 
33     fileMap[wd] = fs::path(path);
34 }
35 
36 SyncWatch::SyncWatch(sd_event& loop,
37                      std::function<int(int, fs::path&)> syncCallback) :
38     inotifyFd(-1),
39     syncCallback(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     auto syncfile = fs::path(SYNC_LIST_DIR_PATH) / SYNC_LIST_FILE_NAME;
57     if (fs::exists(syncfile))
58     {
59         std::string line;
60         std::ifstream file(syncfile.c_str());
61         while (std::getline(file, line))
62         {
63             addInotifyWatch(line);
64         }
65     }
66 }
67 
68 SyncWatch::~SyncWatch()
69 {
70     if (inotifyFd != -1)
71     {
72         close(inotifyFd);
73     }
74 }
75 
76 int SyncWatch::callback(sd_event_source* /* s */, int fd, uint32_t revents,
77                         void* userdata)
78 {
79     if (!(revents & EPOLLIN))
80     {
81         return 0;
82     }
83 
84     constexpr auto maxBytes = 1024;
85     uint8_t buffer[maxBytes];
86     auto bytes = read(fd, buffer, maxBytes);
87     if (0 > bytes)
88     {
89         return 0;
90     }
91 
92     auto syncWatch = static_cast<SyncWatch*>(userdata);
93     auto offset = 0;
94     while (offset < bytes)
95     {
96         auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
97 
98         // Watch was removed, re-add it if file still exists.
99         if (event->mask & IN_IGNORED)
100         {
101             if (fs::exists(syncWatch->fileMap[event->wd]))
102             {
103                 syncWatch->addInotifyWatch(syncWatch->fileMap[event->wd]);
104             }
105             else
106             {
107                 info("The inotify watch on {PATH} was removed", "PATH",
108                      syncWatch->fileMap[event->wd]);
109             }
110             return 0;
111         }
112 
113         // fileMap<wd, path>
114         auto rc =
115             syncWatch->syncCallback(event->mask, syncWatch->fileMap[event->wd]);
116         if (rc)
117         {
118             return rc;
119         }
120 
121         offset += offsetof(inotify_event, name) + event->len;
122     }
123 
124     return 0;
125 }
126 
127 } // namespace manager
128 } // namespace software
129 } // namespace phosphor
130