xref: /openbmc/pldm/fw-update/watch.cpp (revision bdf56f04)
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 
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("inotify_init1 failed, errno="s +
39                                  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("inotify_add_watch failed, errno="s +
48                                  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("failed to add to event loop, rc="s +
55                                  std::strerror(-rc));
56     }
57 }
58 
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 
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("failed to read inotify event, errno="s +
86                                  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