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