1 #include "filesystem_log_watcher.hpp"
2 
3 #include "event_service_manager.hpp"
4 #include "logging.hpp"
5 
6 #include <sys/inotify.h>
7 
8 #include <boost/asio/buffer.hpp>
9 #include <boost/asio/error.hpp>
10 #include <boost/asio/io_context.hpp>
11 #include <boost/asio/posix/stream_descriptor.hpp>
12 
13 #include <array>
14 #include <cstddef>
15 #include <cstring>
16 #include <optional>
17 #include <string>
18 
19 namespace redfish
20 {
21 
22 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
23 static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
24 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
25 static int inotifyFd = -1;
26 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
27 static int dirWatchDesc = -1;
28 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
29 static int fileWatchDesc = -1;
30 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
31 static std::array<char, 1024> readBuffer{};
32 
33 static constexpr const char* redfishEventLogDir = "/var/log";
34 static constexpr const size_t iEventSize = sizeof(inotify_event);
35 
watchRedfishEventLogFile()36 static void watchRedfishEventLogFile()
37 {
38     if (!inotifyConn)
39     {
40         BMCWEB_LOG_ERROR("inotify Connection is not present");
41         return;
42     }
43 
44     inotifyConn->async_read_some(
45         boost::asio::buffer(readBuffer),
46         [&](const boost::system::error_code& ec,
47             const std::size_t& bytesTransferred) {
48             if (ec == boost::asio::error::operation_aborted)
49             {
50                 BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
51                 return;
52             }
53             if (ec)
54             {
55                 BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
56                 return;
57             }
58 
59             BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
60 
61             std::size_t index = 0;
62             while ((index + iEventSize) <= bytesTransferred)
63             {
64                 struct inotify_event event
65                 {};
66                 std::memcpy(&event, &readBuffer[index], iEventSize);
67                 if (event.wd == dirWatchDesc)
68                 {
69                     if ((event.len == 0) ||
70                         (index + iEventSize + event.len > bytesTransferred))
71                     {
72                         index += (iEventSize + event.len);
73                         continue;
74                     }
75 
76                     std::string fileName(&readBuffer[index + iEventSize]);
77                     if (fileName != "redfish")
78                     {
79                         index += (iEventSize + event.len);
80                         continue;
81                     }
82 
83                     BMCWEB_LOG_DEBUG(
84                         "Redfish log file created/deleted. event.name: {}",
85                         fileName);
86                     if (event.mask == IN_CREATE)
87                     {
88                         if (fileWatchDesc != -1)
89                         {
90                             BMCWEB_LOG_DEBUG(
91                                 "Remove and Add inotify watcher on "
92                                 "redfish event log file");
93                             // Remove existing inotify watcher and add
94                             // with new redfish event log file.
95                             inotify_rm_watch(inotifyFd, fileWatchDesc);
96                             fileWatchDesc = -1;
97                         }
98 
99                         fileWatchDesc = inotify_add_watch(
100                             inotifyFd, redfishEventLogFile, IN_MODIFY);
101                         if (fileWatchDesc == -1)
102                         {
103                             BMCWEB_LOG_ERROR("inotify_add_watch failed for "
104                                              "redfish log file.");
105                             return;
106                         }
107 
108                         EventServiceManager::getInstance()
109                             .resetRedfishFilePosition();
110                         EventServiceManager::getInstance()
111                             .readEventLogsFromFile();
112                     }
113                     else if ((event.mask == IN_DELETE) ||
114                              (event.mask == IN_MOVED_TO))
115                     {
116                         if (fileWatchDesc != -1)
117                         {
118                             inotify_rm_watch(inotifyFd, fileWatchDesc);
119                             fileWatchDesc = -1;
120                         }
121                     }
122                 }
123                 else if (event.wd == fileWatchDesc)
124                 {
125                     if (event.mask == IN_MODIFY)
126                     {
127                         EventServiceManager::getInstance()
128                             .readEventLogsFromFile();
129                     }
130                 }
131                 index += (iEventSize + event.len);
132             }
133 
134             watchRedfishEventLogFile();
135         });
136 }
137 
startEventLogMonitor(boost::asio::io_context & ioc)138 int startEventLogMonitor(boost::asio::io_context& ioc)
139 {
140     BMCWEB_LOG_DEBUG("starting Event Log Monitor");
141 
142     inotifyConn.emplace(ioc);
143     inotifyFd = inotify_init1(IN_NONBLOCK);
144     if (inotifyFd == -1)
145     {
146         BMCWEB_LOG_ERROR("inotify_init1 failed.");
147         return -1;
148     }
149 
150     // Add watch on directory to handle redfish event log file
151     // create/delete.
152     dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
153                                      IN_CREATE | IN_MOVED_TO | IN_DELETE);
154     if (dirWatchDesc == -1)
155     {
156         BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
157         return -1;
158     }
159 
160     // Watch redfish event log file for modifications.
161     fileWatchDesc =
162         inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
163     if (fileWatchDesc == -1)
164     {
165         BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
166         // Don't return error if file not exist.
167         // Watch on directory will handle create/delete of file.
168     }
169 
170     // monitor redfish event log file
171     inotifyConn->assign(inotifyFd);
172     watchRedfishEventLogFile();
173 
174     return 0;
175 }
176 
stopEventLogMonitor()177 void stopEventLogMonitor()
178 {
179     inotifyConn.reset();
180 }
181 } // namespace redfish
182