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