1 #include "watch.hpp"
2
3 #include <sys/inotify.h>
4 #include <unistd.h>
5
6 #include <cstddef>
7 #include <cstring>
8 #include <filesystem>
9 #include <stdexcept>
10 #include <string>
11
12 namespace pldm
13 {
14
15 namespace fw_update
16 {
17
18 // using namespace phosphor::logging;
19 using namespace std::string_literals;
20 namespace fs = std::filesystem;
21
Watch(sd_event * loop,std::function<int (std::string &)> imageCallback)22 Watch::Watch(sd_event* loop, std::function<int(std::string&)> imageCallback) :
23 imageCallback(imageCallback)
24 {
25 // Check if IMAGE DIR exists.
26 fs::path imgDirPath("/tmp/images");
27 if (!fs::is_directory(imgDirPath))
28 {
29 fs::create_directories(imgDirPath);
30 }
31
32 fd = inotify_init1(IN_NONBLOCK);
33 if (-1 == fd)
34 {
35 // Store a copy of errno, because the string creation below will
36 // invalidate errno due to one more system calls.
37 auto error = errno;
38 throw std::runtime_error(
39 "inotify_init1 failed, errno="s + std::strerror(error));
40 }
41
42 wd = inotify_add_watch(fd, "/tmp/images", IN_CLOSE_WRITE);
43 if (-1 == wd)
44 {
45 auto error = errno;
46 close(fd);
47 throw std::runtime_error(
48 "inotify_add_watch failed, errno="s + std::strerror(error));
49 }
50
51 auto rc = sd_event_add_io(loop, nullptr, fd, EPOLLIN, callback, this);
52 if (0 > rc)
53 {
54 throw std::runtime_error(
55 "failed to add to event loop, rc="s + std::strerror(-rc));
56 }
57 }
58
~Watch()59 Watch::~Watch()
60 {
61 if (-1 != fd)
62 {
63 if (-1 != wd)
64 {
65 inotify_rm_watch(fd, wd);
66 }
67 close(fd);
68 }
69 }
70
callback(sd_event_source *,int fd,uint32_t revents,void * userdata)71 int Watch::callback(sd_event_source* /* s */, int fd, 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::runtime_error(
86 "failed to read inotify event, errno="s + std::strerror(error));
87 }
88
89 auto offset = 0;
90 while (offset < bytes)
91 {
92 auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
93 if ((event->mask & IN_CLOSE_WRITE) && !(event->mask & IN_ISDIR))
94 {
95 auto tarballPath = std::string{"/tmp/images"} + '/' + event->name;
96 auto rc = static_cast<Watch*>(userdata)->imageCallback(tarballPath);
97 if (rc < 0)
98 {
99 // log<level::ERR>("Error processing image",
100 // entry("IMAGE=%s", tarballPath.c_str()));
101 }
102 }
103
104 offset += offsetof(inotify_event, name) + event->len;
105 }
106
107 return 0;
108 }
109
110 } // namespace fw_update
111 } // namespace pldm
112