xref: /openbmc/bmcweb/features/redfish/src/filesystem_log_watcher.cpp (revision ea20bc6610b4e8cbd2b143221b2917c5e6cf18b6)
12185ddeaSEd Tanous #include "filesystem_log_watcher.hpp"
22185ddeaSEd Tanous 
32185ddeaSEd Tanous #include "event_service_manager.hpp"
42185ddeaSEd Tanous #include "logging.hpp"
52185ddeaSEd Tanous 
62185ddeaSEd Tanous #include <sys/inotify.h>
72185ddeaSEd Tanous 
82185ddeaSEd Tanous #include <boost/asio/buffer.hpp>
92185ddeaSEd Tanous #include <boost/asio/error.hpp>
102185ddeaSEd Tanous #include <boost/asio/io_context.hpp>
112185ddeaSEd Tanous #include <boost/asio/posix/stream_descriptor.hpp>
122185ddeaSEd Tanous 
132185ddeaSEd Tanous #include <array>
142185ddeaSEd Tanous #include <cstddef>
152185ddeaSEd Tanous #include <cstring>
162185ddeaSEd Tanous #include <optional>
172185ddeaSEd Tanous #include <string>
182185ddeaSEd Tanous 
192185ddeaSEd Tanous namespace redfish
202185ddeaSEd Tanous {
212185ddeaSEd Tanous 
222185ddeaSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
232185ddeaSEd Tanous static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
242185ddeaSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
252185ddeaSEd Tanous static int inotifyFd = -1;
262185ddeaSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
272185ddeaSEd Tanous static int dirWatchDesc = -1;
282185ddeaSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
292185ddeaSEd Tanous static int fileWatchDesc = -1;
302185ddeaSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
312185ddeaSEd Tanous static std::array<char, 1024> readBuffer{};
322185ddeaSEd Tanous 
332185ddeaSEd Tanous static constexpr const char* redfishEventLogDir = "/var/log";
342185ddeaSEd Tanous static constexpr const size_t iEventSize = sizeof(inotify_event);
352185ddeaSEd Tanous 
36*ea20bc66SEd Tanous static void watchRedfishEventLogFile();
372185ddeaSEd Tanous 
38*ea20bc66SEd Tanous static void onINotify(const boost::system::error_code& ec,
39*ea20bc66SEd Tanous                       std::size_t bytesTransferred)
40*ea20bc66SEd Tanous {
412185ddeaSEd Tanous     if (ec == boost::asio::error::operation_aborted)
422185ddeaSEd Tanous     {
432185ddeaSEd Tanous         BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
442185ddeaSEd Tanous         return;
452185ddeaSEd Tanous     }
462185ddeaSEd Tanous     if (ec)
472185ddeaSEd Tanous     {
482185ddeaSEd Tanous         BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
492185ddeaSEd Tanous         return;
502185ddeaSEd Tanous     }
512185ddeaSEd Tanous 
522185ddeaSEd Tanous     BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
532185ddeaSEd Tanous 
542185ddeaSEd Tanous     std::size_t index = 0;
552185ddeaSEd Tanous     while ((index + iEventSize) <= bytesTransferred)
562185ddeaSEd Tanous     {
572185ddeaSEd Tanous         struct inotify_event event
582185ddeaSEd Tanous         {};
592185ddeaSEd Tanous         std::memcpy(&event, &readBuffer[index], iEventSize);
602185ddeaSEd Tanous         if (event.wd == dirWatchDesc)
612185ddeaSEd Tanous         {
622185ddeaSEd Tanous             if ((event.len == 0) ||
632185ddeaSEd Tanous                 (index + iEventSize + event.len > bytesTransferred))
642185ddeaSEd Tanous             {
652185ddeaSEd Tanous                 index += (iEventSize + event.len);
662185ddeaSEd Tanous                 continue;
672185ddeaSEd Tanous             }
682185ddeaSEd Tanous 
692185ddeaSEd Tanous             std::string fileName(&readBuffer[index + iEventSize]);
702185ddeaSEd Tanous             if (fileName != "redfish")
712185ddeaSEd Tanous             {
722185ddeaSEd Tanous                 index += (iEventSize + event.len);
732185ddeaSEd Tanous                 continue;
742185ddeaSEd Tanous             }
752185ddeaSEd Tanous 
76*ea20bc66SEd Tanous             BMCWEB_LOG_DEBUG("Redfish log file created/deleted. event.name: {}",
772185ddeaSEd Tanous                              fileName);
782185ddeaSEd Tanous             if (event.mask == IN_CREATE)
792185ddeaSEd Tanous             {
802185ddeaSEd Tanous                 if (fileWatchDesc != -1)
812185ddeaSEd Tanous                 {
82*ea20bc66SEd Tanous                     BMCWEB_LOG_DEBUG("Remove and Add inotify watcher on "
832185ddeaSEd Tanous                                      "redfish event log file");
842185ddeaSEd Tanous                     // Remove existing inotify watcher and add
852185ddeaSEd Tanous                     // with new redfish event log file.
862185ddeaSEd Tanous                     inotify_rm_watch(inotifyFd, fileWatchDesc);
872185ddeaSEd Tanous                     fileWatchDesc = -1;
882185ddeaSEd Tanous                 }
892185ddeaSEd Tanous 
902185ddeaSEd Tanous                 fileWatchDesc = inotify_add_watch(
912185ddeaSEd Tanous                     inotifyFd, redfishEventLogFile, IN_MODIFY);
922185ddeaSEd Tanous                 if (fileWatchDesc == -1)
932185ddeaSEd Tanous                 {
942185ddeaSEd Tanous                     BMCWEB_LOG_ERROR("inotify_add_watch failed for "
952185ddeaSEd Tanous                                      "redfish log file.");
962185ddeaSEd Tanous                     return;
972185ddeaSEd Tanous                 }
982185ddeaSEd Tanous 
99*ea20bc66SEd Tanous                 EventServiceManager::getInstance().resetRedfishFilePosition();
100*ea20bc66SEd Tanous                 EventServiceManager::getInstance().readEventLogsFromFile();
1012185ddeaSEd Tanous             }
102*ea20bc66SEd Tanous             else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO))
1032185ddeaSEd Tanous             {
1042185ddeaSEd Tanous                 if (fileWatchDesc != -1)
1052185ddeaSEd Tanous                 {
1062185ddeaSEd Tanous                     inotify_rm_watch(inotifyFd, fileWatchDesc);
1072185ddeaSEd Tanous                     fileWatchDesc = -1;
1082185ddeaSEd Tanous                 }
1092185ddeaSEd Tanous             }
1102185ddeaSEd Tanous         }
1112185ddeaSEd Tanous         else if (event.wd == fileWatchDesc)
1122185ddeaSEd Tanous         {
1132185ddeaSEd Tanous             if (event.mask == IN_MODIFY)
1142185ddeaSEd Tanous             {
115*ea20bc66SEd Tanous                 EventServiceManager::getInstance().readEventLogsFromFile();
1162185ddeaSEd Tanous             }
1172185ddeaSEd Tanous         }
1182185ddeaSEd Tanous         index += (iEventSize + event.len);
1192185ddeaSEd Tanous     }
1202185ddeaSEd Tanous 
1212185ddeaSEd Tanous     watchRedfishEventLogFile();
122*ea20bc66SEd Tanous }
123*ea20bc66SEd Tanous 
124*ea20bc66SEd Tanous static void watchRedfishEventLogFile()
125*ea20bc66SEd Tanous {
126*ea20bc66SEd Tanous     if (!inotifyConn)
127*ea20bc66SEd Tanous     {
128*ea20bc66SEd Tanous         BMCWEB_LOG_ERROR("inotify Connection is not present");
129*ea20bc66SEd Tanous         return;
130*ea20bc66SEd Tanous     }
131*ea20bc66SEd Tanous 
132*ea20bc66SEd Tanous     inotifyConn->async_read_some(boost::asio::buffer(readBuffer), onINotify);
1332185ddeaSEd Tanous }
1342185ddeaSEd Tanous 
1352185ddeaSEd Tanous int startEventLogMonitor(boost::asio::io_context& ioc)
1362185ddeaSEd Tanous {
1372185ddeaSEd Tanous     BMCWEB_LOG_DEBUG("starting Event Log Monitor");
1382185ddeaSEd Tanous 
1392185ddeaSEd Tanous     inotifyConn.emplace(ioc);
1402185ddeaSEd Tanous     inotifyFd = inotify_init1(IN_NONBLOCK);
1412185ddeaSEd Tanous     if (inotifyFd == -1)
1422185ddeaSEd Tanous     {
1432185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_init1 failed.");
1442185ddeaSEd Tanous         return -1;
1452185ddeaSEd Tanous     }
1462185ddeaSEd Tanous 
1472185ddeaSEd Tanous     // Add watch on directory to handle redfish event log file
1482185ddeaSEd Tanous     // create/delete.
1492185ddeaSEd Tanous     dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
1502185ddeaSEd Tanous                                      IN_CREATE | IN_MOVED_TO | IN_DELETE);
1512185ddeaSEd Tanous     if (dirWatchDesc == -1)
1522185ddeaSEd Tanous     {
1532185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
1542185ddeaSEd Tanous         return -1;
1552185ddeaSEd Tanous     }
1562185ddeaSEd Tanous 
1572185ddeaSEd Tanous     // Watch redfish event log file for modifications.
1582185ddeaSEd Tanous     fileWatchDesc =
1592185ddeaSEd Tanous         inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
1602185ddeaSEd Tanous     if (fileWatchDesc == -1)
1612185ddeaSEd Tanous     {
1622185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
1632185ddeaSEd Tanous         // Don't return error if file not exist.
1642185ddeaSEd Tanous         // Watch on directory will handle create/delete of file.
1652185ddeaSEd Tanous     }
1662185ddeaSEd Tanous 
1672185ddeaSEd Tanous     // monitor redfish event log file
1682185ddeaSEd Tanous     inotifyConn->assign(inotifyFd);
1692185ddeaSEd Tanous     watchRedfishEventLogFile();
1702185ddeaSEd Tanous 
1712185ddeaSEd Tanous     return 0;
1722185ddeaSEd Tanous }
1732185ddeaSEd Tanous 
1742185ddeaSEd Tanous void stopEventLogMonitor()
1752185ddeaSEd Tanous {
1762185ddeaSEd Tanous     inotifyConn.reset();
1772185ddeaSEd Tanous }
1782185ddeaSEd Tanous } // namespace redfish
179