1 #include "config.h"
2 
3 #include "watch.hpp"
4 
5 #include "item_updater_ubi.hpp"
6 
7 #include <sys/inotify.h>
8 #include <unistd.h>
9 
10 #include <cstddef>
11 #include <cstring>
12 #include <experimental/filesystem>
13 #include <functional>
14 #include <phosphor-logging/log.hpp>
15 #include <stdexcept>
16 #include <string>
17 
18 namespace openpower
19 {
20 namespace software
21 {
22 namespace updater
23 {
24 
25 using namespace phosphor::logging;
26 namespace fs = std::experimental::filesystem;
27 
28 Watch::Watch(sd_event* loop,
29              std::function<void(const std::string&)> functionalCallback) :
30     functionalCallback(functionalCallback),
31     fd(inotifyInit())
32 
33 {
34     // Create PNOR_ACTIVE_PATH if doesn't exist.
35     if (!fs::is_directory(PNOR_ACTIVE_PATH))
36     {
37         fs::create_directories(PNOR_ACTIVE_PATH);
38     }
39 
40     wd = inotify_add_watch(fd(), PNOR_ACTIVE_PATH, IN_CREATE);
41     if (-1 == wd)
42     {
43         auto error = errno;
44         throw std::system_error(error, std::generic_category(),
45                                 "Error occurred during the inotify_init1");
46     }
47 
48     decltype(eventSource.get()) sourcePtr = nullptr;
49     auto rc = sd_event_add_io(loop, &sourcePtr, fd(), EPOLLIN, callback, this);
50 
51     eventSource.reset(sourcePtr);
52 
53     if (0 > rc)
54     {
55         throw std::system_error(-rc, std::generic_category(),
56                                 "Error occurred during the inotify_init1");
57     }
58 }
59 
60 Watch::~Watch()
61 {
62     if ((-1 != fd()) && (-1 != wd))
63     {
64         inotify_rm_watch(fd(), wd);
65     }
66 }
67 
68 int Watch::callback(sd_event_source* s, int fd, uint32_t revents,
69                     void* userdata)
70 {
71     if (!(revents & EPOLLIN))
72     {
73         return 0;
74     }
75 
76     constexpr auto maxBytes = 1024;
77     uint8_t buffer[maxBytes];
78     auto bytes = read(fd, buffer, maxBytes);
79     if (0 > bytes)
80     {
81         auto error = errno;
82         throw std::system_error(error, std::generic_category(),
83                                 "failed to read inotify event");
84     }
85 
86     auto offset = 0;
87     while (offset < bytes)
88     {
89         auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
90         // Update the functional association on a RO
91         // active image symlink change
92         fs::path path(PNOR_ACTIVE_PATH);
93         path /= event->name;
94         if (fs::equivalent(path, PNOR_RO_ACTIVE_PATH))
95         {
96             auto id = ItemUpdaterUbi::determineId(path);
97             static_cast<Watch*>(userdata)->functionalCallback(id);
98         }
99         offset += offsetof(inotify_event, name) + event->len;
100     }
101 
102     return 0;
103 }
104 
105 int Watch::inotifyInit()
106 {
107     auto fd = inotify_init1(IN_NONBLOCK);
108 
109     if (-1 == fd)
110     {
111         auto error = errno;
112         throw std::system_error(error, std::generic_category(),
113                                 "Error occurred during the inotify_init1");
114     }
115 
116     return fd;
117 }
118 
119 } // namespace updater
120 } // namespace software
121 } // namespace openpower
122