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