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