FlatImage
A configurable Linux containerization system
Loading...
Searching...
No Matches
linux.hpp
Go to the documentation of this file.
1
8
9#pragma once
10
11#include <chrono>
12#include <csignal>
13#include <cstring>
14#include <fcntl.h>
15#include <poll.h>
16#include <string>
17#include <filesystem>
18#include <sys/stat.h>
19#include <sys/time.h>
20#include <unistd.h>
21#include <cassert>
22#include <thread>
23
24#include "../macro.hpp"
25#include "../std/expected.hpp"
26
35namespace ns_linux
36{
37
38namespace
39{
40
41namespace fs = std::filesystem;
42
43}
44
53[[nodiscard]] inline bool poll_with_timeout(int fd, short events, std::chrono::milliseconds const& timeout)
54{
55 struct pollfd pfd;
56 pfd.fd = fd;
57 pfd.events = events;
58
59 int poll_result = ::poll(&pfd, 1, timeout.count());
60
61 if (poll_result < 0)
62 {
63 // poll() error - errno already set
64 return false;
65 }
66 else if (poll_result == 0)
67 {
68 // Timeout
69 errno = ETIMEDOUT;
70 return false;
71 }
72
73 // Ready for I/O
74 return true;
75}
76
88template<typename Data>
89[[nodiscard]] inline ssize_t read_with_timeout(int fd
90 , std::chrono::milliseconds const& timeout
91 , std::span<Data> buf)
92{
93 using Element = typename std::decay_t<typename decltype(buf)::value_type>;
94
95 if (!poll_with_timeout(fd, POLLIN, timeout))
96 {
97 return -1;
98 }
99
100 return ::read(fd, buf.data(), buf.size() * sizeof(Element));
101}
102
114template<typename Data>
115[[nodiscard]] inline ssize_t write_with_timeout(int fd
116 , std::chrono::milliseconds const& timeout
117 , std::span<Data> buf)
118{
119 using Element = typename std::decay_t<typename decltype(buf)::value_type>;
120
121 if (!poll_with_timeout(fd, POLLOUT, timeout))
122 {
123 return -1;
124 }
125
126 return ::write(fd, buf.data(), buf.size() * sizeof(Element));
127}
128
143[[nodiscard]] inline int open_with_timeout(
144 fs::path const& path_file_src,
145 std::chrono::milliseconds timeout,
146 int oflag)
147{
148 if(struct stat st; ::stat(path_file_src.c_str(), &st) != 0)
149 {
150 return -1;
151 }
152 // Check if this is a FIFO first
153 else if (not S_ISFIFO(st.st_mode))
154 {
155 // For regular files, open normally
156 return ::open(path_file_src.c_str(), oflag);
157 }
158 // For FIFOs: use O_NONBLOCK to avoid blocking
159 // For O_WRONLY: open() fails with ENXIO if no reader, so we retry
160 // For O_RDONLY: open() succeeds immediately
161 for(auto start = std::chrono::steady_clock::now();;)
162 {
163 if (int fd = ::open(path_file_src.c_str(), oflag | O_NONBLOCK); fd >= 0)
164 {
165 // Successfully opened - remove O_NONBLOCK if not requested
166 if (!(oflag & O_NONBLOCK))
167 {
168 int flags = ::fcntl(fd, F_GETFL);
169 if (flags >= 0)
170 {
171 ::fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
172 }
173 }
174 return fd;
175 }
176 // Check if this is a retry-able error (ENXIO for O_WRONLY means no reader yet)
177 if (errno == ENXIO and (oflag & O_WRONLY))
178 {
179 // Check timeout
180 auto elapsed = std::chrono::steady_clock::now() - start;
181 if (elapsed >= timeout)
182 {
183 errno = ETIMEDOUT;
184 return -1;
185 }
186 // Retry after a short delay
187 std::this_thread::sleep_for(std::chrono::milliseconds(10));
188 continue;
189 }
190 // Other errors - return immediately
191 return -1;
192 }
193}
194
204template<typename Data>
205[[nodiscard]] inline ssize_t open_read_with_timeout(fs::path const& path_file_src
206 , std::chrono::milliseconds const& timeout
207 , std::span<Data> buf)
208{
209 int fd = open_with_timeout(path_file_src, timeout, O_RDONLY);
210 return_if(fd < 0, fd);
211 ssize_t bytes_read = read_with_timeout(fd, timeout, buf);
212 close(fd);
213 return bytes_read;
214}
215
225template<typename Data>
226[[nodiscard]] inline ssize_t open_write_with_timeout(fs::path const& path_file_src
227 , std::chrono::milliseconds const& timeout
228 , std::span<Data> buf)
229{
230 int fd = open_with_timeout(path_file_src, timeout, O_WRONLY);
231 return_if(fd < 0, fd);
232 ssize_t bytes_written = write_with_timeout(fd, timeout, buf);
233 close(fd);
234 return bytes_written;
235}
236
243[[nodiscard]] inline Value<bool> module_check(std::string_view str_name)
244{
245 std::ifstream file_modules("/proc/modules");
246 return_if(not file_modules.is_open(), Error("E::Could not open modules file"));
247
248 std::string line;
249 while ( std::getline(file_modules, line) )
250 {
251 return_if(line.contains(str_name), true);
252 }
253
254 return false;
255}
256
257} // namespace ns_linux
258
259/* vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et :*/
Enhanced error handling framework built on std::expected.
Simplified macros for common control flow patterns with optional logging.
Linux-specific system operations.
int open_with_timeout(fs::path const &path_file_src, std::chrono::milliseconds timeout, int oflag)
Opens a given file with a timeout.
Definition linux.hpp:143
bool poll_with_timeout(int fd, short events, std::chrono::milliseconds const &timeout)
Waits for a file descriptor to be ready for I/O with a timeout.
Definition linux.hpp:53
ssize_t open_write_with_timeout(fs::path const &path_file_src, std::chrono::milliseconds const &timeout, std::span< Data > buf)
Opens and writes to the given input file.
Definition linux.hpp:226
Value< bool > module_check(std::string_view str_name)
Checks if the linux kernel has a module loaded that matches the input name.
Definition linux.hpp:243
ssize_t write_with_timeout(int fd, std::chrono::milliseconds const &timeout, std::span< Data > buf)
Writes to the file descriptor with a timeout.
Definition linux.hpp:115
ssize_t open_read_with_timeout(fs::path const &path_file_src, std::chrono::milliseconds const &timeout, std::span< Data > buf)
Opens and reads from the given input file.
Definition linux.hpp:205
ssize_t read_with_timeout(int fd, std::chrono::milliseconds const &timeout, std::span< Data > buf)
Reads from the file descriptor with a timeout.
Definition linux.hpp:89
Enhanced expected type with integrated logging capabilities.
Definition expected.hpp:44