xref: /openbmc/bmcweb/features/redfish/src/filesystem_log_watcher.cpp (revision ea20bc6610b4e8cbd2b143221b2917c5e6cf18b6)
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 
36 static void watchRedfishEventLogFile();
37 
38 static void onINotify(const boost::system::error_code& ec,
39                       std::size_t bytesTransferred)
40 {
41     if (ec == boost::asio::error::operation_aborted)
42     {
43         BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
44         return;
45     }
46     if (ec)
47     {
48         BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
49         return;
50     }
51 
52     BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
53 
54     std::size_t index = 0;
55     while ((index + iEventSize) <= bytesTransferred)
56     {
57         struct inotify_event event
58         {};
59         std::memcpy(&event, &readBuffer[index], iEventSize);
60         if (event.wd == dirWatchDesc)
61         {
62             if ((event.len == 0) ||
63                 (index + iEventSize + event.len > bytesTransferred))
64             {
65                 index += (iEventSize + event.len);
66                 continue;
67             }
68 
69             std::string fileName(&readBuffer[index + iEventSize]);
70             if (fileName != "redfish")
71             {
72                 index += (iEventSize + event.len);
73                 continue;
74             }
75 
76             BMCWEB_LOG_DEBUG("Redfish log file created/deleted. event.name: {}",
77                              fileName);
78             if (event.mask == IN_CREATE)
79             {
80                 if (fileWatchDesc != -1)
81                 {
82                     BMCWEB_LOG_DEBUG("Remove and Add inotify watcher on "
83                                      "redfish event log file");
84                     // Remove existing inotify watcher and add
85                     // with new redfish event log file.
86                     inotify_rm_watch(inotifyFd, fileWatchDesc);
87                     fileWatchDesc = -1;
88                 }
89 
90                 fileWatchDesc = inotify_add_watch(
91                     inotifyFd, redfishEventLogFile, IN_MODIFY);
92                 if (fileWatchDesc == -1)
93                 {
94                     BMCWEB_LOG_ERROR("inotify_add_watch failed for "
95                                      "redfish log file.");
96                     return;
97                 }
98 
99                 EventServiceManager::getInstance().resetRedfishFilePosition();
100                 EventServiceManager::getInstance().readEventLogsFromFile();
101             }
102             else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO))
103             {
104                 if (fileWatchDesc != -1)
105                 {
106                     inotify_rm_watch(inotifyFd, fileWatchDesc);
107                     fileWatchDesc = -1;
108                 }
109             }
110         }
111         else if (event.wd == fileWatchDesc)
112         {
113             if (event.mask == IN_MODIFY)
114             {
115                 EventServiceManager::getInstance().readEventLogsFromFile();
116             }
117         }
118         index += (iEventSize + event.len);
119     }
120 
121     watchRedfishEventLogFile();
122 }
123 
124 static void watchRedfishEventLogFile()
125 {
126     if (!inotifyConn)
127     {
128         BMCWEB_LOG_ERROR("inotify Connection is not present");
129         return;
130     }
131 
132     inotifyConn->async_read_some(boost::asio::buffer(readBuffer), onINotify);
133 }
134 
135 int startEventLogMonitor(boost::asio::io_context& ioc)
136 {
137     BMCWEB_LOG_DEBUG("starting Event Log Monitor");
138 
139     inotifyConn.emplace(ioc);
140     inotifyFd = inotify_init1(IN_NONBLOCK);
141     if (inotifyFd == -1)
142     {
143         BMCWEB_LOG_ERROR("inotify_init1 failed.");
144         return -1;
145     }
146 
147     // Add watch on directory to handle redfish event log file
148     // create/delete.
149     dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
150                                      IN_CREATE | IN_MOVED_TO | IN_DELETE);
151     if (dirWatchDesc == -1)
152     {
153         BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
154         return -1;
155     }
156 
157     // Watch redfish event log file for modifications.
158     fileWatchDesc =
159         inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
160     if (fileWatchDesc == -1)
161     {
162         BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
163         // Don't return error if file not exist.
164         // Watch on directory will handle create/delete of file.
165     }
166 
167     // monitor redfish event log file
168     inotifyConn->assign(inotifyFd);
169     watchRedfishEventLogFile();
170 
171     return 0;
172 }
173 
174 void stopEventLogMonitor()
175 {
176     inotifyConn.reset();
177 }
178 } // namespace redfish
179