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 <phosphor-logging/log.hpp>
11 
12 #include <cstddef>
13 #include <cstring>
14 #include <filesystem>
15 #include <functional>
16 #include <stdexcept>
17 #include <string>
18 
19 namespace openpower
20 {
21 namespace software
22 {
23 namespace updater
24 {
25 
26 using namespace phosphor::logging;
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 (!std::filesystem::is_directory(PNOR_ACTIVE_PATH))
36     {
37         std::filesystem::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*, int fd, uint32_t revents, void* userdata)
69 {
70     if (!(revents & EPOLLIN))
71     {
72         return 0;
73     }
74 
75     constexpr auto maxBytes = 1024;
76     uint8_t buffer[maxBytes];
77     auto bytes = read(fd, buffer, maxBytes);
78     if (0 > bytes)
79     {
80         auto error = errno;
81         throw std::system_error(error, std::generic_category(),
82                                 "failed to read inotify event");
83     }
84 
85     auto offset = 0;
86     while (offset < bytes)
87     {
88         auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
89         // Update the functional association on a RO
90         // active image symlink change
91         std::filesystem::path path(PNOR_ACTIVE_PATH);
92         path /= event->name;
93         if (std::filesystem::equivalent(path, PNOR_RO_ACTIVE_PATH))
94         {
95             auto id = ItemUpdaterUbi::determineId(path);
96             static_cast<Watch*>(userdata)->functionalCallback(id);
97         }
98         offset += offsetof(inotify_event, name) + event->len;
99     }
100 
101     return 0;
102 }
103 
104 int Watch::inotifyInit()
105 {
106     auto fd = inotify_init1(IN_NONBLOCK);
107 
108     if (-1 == fd)
109     {
110         auto error = errno;
111         throw std::system_error(error, std::generic_category(),
112                                 "Error occurred during the inotify_init1");
113     }
114 
115     return fd;
116 }
117 
118 } // namespace updater
119 } // namespace software
120 } // namespace openpower
121