Repo-Init
 
Loading...
Searching...
No Matches
FileHelpers.cpp
Go to the documentation of this file.
2
4
5#include <format>
6#include <iostream>
7
8#include <fcntl.h>
9#include <sys/file.h>
10#include <sys/ioctl.h>
11#include <unistd.h>
12
13#include <spdlog/spdlog.h>
14
16constexpr int SLEEP_INTERVAL_MS = 50;
17
18void FileMonitor::threadFunc(const std::stop_token &stopToken) const noexcept
19{
20 while (!stopToken.stop_requested())
21 {
22 // Buffer for reading events
23 unsigned int nBytes = 0;
24 if (ioctl(_fDescriptor, FIONREAD, &nBytes) < 0)
25 {
26 spdlog::error("Failed to get available events for file monitoring: {}", getErrnoString(errno));
27 }
28
29 auto buffer = std::vector<char>(nBytes + 1, '\0');
30 auto nRead = read(_fDescriptor, buffer.data(), nBytes);
31 if (nRead < 0 && errno != EAGAIN)
32 {
33 spdlog::error("Failed to read events for file monitoring: {}", getErrnoString(errno));
34 }
35 else if (nRead == 0)
36 {
37 spdlog::debug("No events read for file monitoring");
38 }
39
40 ssize_t idx = 0;
41 while (_notifyCallback && idx < nRead)
42 {
43 const auto *event = std::bit_cast<inotify_event *>(&buffer[static_cast<size_t>(idx)]);
44
45 // Check if file notify type matches
46 if ((event->mask & _notifyEvents) != 0)
47 {
49 break;
50 }
51
52 idx += static_cast<ssize_t>(sizeof(inotify_event) + event->len);
53 }
54
55 std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_INTERVAL_MS));
56 }
57}
58
59FileMonitor::FileMonitor(std::filesystem::path filePath, uint32_t notifyEvents)
60 : _fDescriptor(inotify_init()), _filePath(std::move(filePath)), _notifyEvents(notifyEvents)
61{
62 if (_fDescriptor < 0)
63 {
64 throw std::ios_base::failure("Failed to initialize inotify");
65 }
66
67 _wDescriptor = inotify_add_watch(_fDescriptor, _filePath.c_str(), notifyEvents);
68 if (_wDescriptor < 0)
69 {
70 close(_fDescriptor);
71 throw std::ios_base::failure("Failed to add watch descriptor");
72 }
73
74 if (const int flags = fcntl(_fDescriptor, F_GETFL);
75 flags < 0 || fcntl(_fDescriptor, F_SETFL, flags | O_NONBLOCK) < 0)
76 {
77 close(_fDescriptor);
78 throw std::ios_base::failure("Failed to set file descriptor to non-blocking mode");
79 }
80
81 _thread = std::make_unique<std::jthread>([this](const std::stop_token &sToken) { threadFunc(sToken); });
82}
83
85{
86 if (_thread)
87 {
88 _thread.reset();
89 }
90
91 // Remove watch descriptor first
92 if (_wDescriptor >= 0)
93 {
94 if (inotify_rm_watch(_fDescriptor, _wDescriptor) < 0)
95 {
96 try
97 {
98 spdlog::error("Failed to remove watch descriptor: {}", getErrnoString(errno));
99 }
100 catch (const std::exception &e)
101 {
102 std::cerr << "Failed to remove watch descriptor and also logger thrown an exception: "
103 << getErrnoString(errno) << " " << e.what() << '\n';
104 }
105 }
106 _wDescriptor = -1;
107 }
108
109 // Then close the file descriptor
110 if (_fDescriptor >= 0)
111 {
112 if (close(_fDescriptor) < 0)
113 {
114 try
115 {
116 spdlog::error("Failed to close file descriptor: {}", getErrnoString(errno));
117 }
118 catch (const std::exception &e)
119 {
120 std::cerr << "Failed to close file descriptor and also logger thrown an exception: "
121 << getErrnoString(errno) << " " << e.what() << '\n';
122 }
123 }
124 _fDescriptor = -1;
125 }
126}
std::string getErrnoString(int errVal)
constexpr int SLEEP_INTERVAL_MS
const void * _userPtr
User pointer.
void threadFunc(const std::stop_token &stopToken) const noexcept
FNotifyCallback _notifyCallback
Callback function.
int _fDescriptor
File descriptor.
FileMonitor(std::filesystem::path filePath, uint32_t notifyEvents=IN_MODIFY)
int _wDescriptor
Watch descriptor.
uint32_t _notifyEvents
Notify types.
std::filesystem::path _filePath
File path.
std::unique_ptr< std::jthread > _thread
Thread.