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