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