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