xref: /openbmc/phosphor-bmc-code-mgmt/bmc/watch.cpp (revision cab87e9cdeeb3e166d6d577511f6be4dc7721aca)
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  
Watch(sd_event * loop,std::function<int (std::string &)> imageCallback)30  Watch::Watch(sd_event* loop, std::function<int(std::string&)> imageCallback) :
31      imageCallback(std::move(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(
48              "inotify_init1 failed, errno="s + 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(
57              "inotify_add_watch failed, errno="s + 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(
64              "failed to add to event loop, rc="s + std::strerror(-rc));
65      }
66  }
67  
~Watch()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  
callback(sd_event_source *,int fd,uint32_t revents,void * userdata)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(
95              "failed to read inotify event, errno="s + 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