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