Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
signal_manager.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
12#pragma once
13
16#include <csignal>
17#include <set>
18#include <mutex>
19#include <atomic>
20
21// Platform-specific headers and definitions
22#ifdef _WIN32
23 #include <io.h>
24 #ifndef STDERR_FILENO
25 #define STDERR_FILENO 2
26 #endif
27 using ssize_t = intptr_t;
28#else
29 #include <unistd.h>
30#endif
31
33
34namespace detail {
35
46inline ssize_t safe_write(int fd, const void* buf, size_t count) {
47#ifdef _WIN32
48 return _write(fd, buf, static_cast<unsigned int>(count));
49#else
50 return ::write(fd, buf, count);
51#endif
52}
53
61inline int safe_fsync(int fd) {
62#ifdef _WIN32
63 return _commit(fd);
64#else
65 return ::fsync(fd);
66#endif
67}
68
69} // namespace detail
70
85public:
89 signal_manager() = default;
90
94 ~signal_manager() override = default;
95
103 std::lock_guard lock(mutex_);
104
105 loggers_.insert(log);
106
107 // Install handlers only on first registration
108 if (loggers_.size() == 1 && !handlers_installed_.load()) {
109 install_handlers();
110 }
111 }
112
120 std::lock_guard lock(mutex_);
121
122 loggers_.erase(log);
123
124 // Remove handlers when last logger is unregistered
125 if (loggers_.empty() && handlers_installed_.load()) {
126 uninstall_handlers();
127 }
128 }
129
133 bool are_handlers_installed() const override {
134 return handlers_installed_.load();
135 }
136
140 size_t logger_count() const override {
141 std::lock_guard lock(mutex_);
142 return loggers_.size();
143 }
144
145 // Prevent copying and moving
150
151private:
152
157 // Set current instance for signal handler
158 current_instance_.store(this, std::memory_order_release);
159
160 // Save original handlers
161 original_sigsegv_ = std::signal(SIGSEGV, signal_handler);
162 original_sigabrt_ = std::signal(SIGABRT, signal_handler);
163 original_sigterm_ = std::signal(SIGTERM, signal_handler);
164 original_sigint_ = std::signal(SIGINT, signal_handler);
165
166 handlers_installed_.store(true);
167 }
168
173 // Restore original handlers
174 std::signal(SIGSEGV, original_sigsegv_);
175 std::signal(SIGABRT, original_sigabrt_);
176 std::signal(SIGTERM, original_sigterm_);
177 std::signal(SIGINT, original_sigint_);
178
179 handlers_installed_.store(false);
180
181 // Clear current instance
182 current_instance_.store(nullptr, std::memory_order_release);
183 }
184
198 static void signal_handler(int sig) {
199 // Write signal message using only signal-safe functions
200 const char* msg = nullptr;
201 size_t msg_len = 0;
202
203 switch (sig) {
204 case SIGSEGV:
205 msg = "\n[CRITICAL] SIGSEGV received - emergency flush\n";
206 msg_len = 45;
207 break;
208 case SIGABRT:
209 msg = "\n[CRITICAL] SIGABRT received - emergency flush\n";
210 msg_len = 45;
211 break;
212 case SIGTERM:
213 msg = "\n[CRITICAL] SIGTERM received - emergency flush\n";
214 msg_len = 45;
215 break;
216 case SIGINT:
217 msg = "\n[CRITICAL] SIGINT received - emergency flush\n";
218 msg_len = 44;
219 break;
220 default:
221 msg = "\n[CRITICAL] Unknown signal - emergency flush\n";
222 msg_len = 43;
223 break;
224 }
225
226 // Write to stderr (signal-safe)
227 detail::safe_write(STDERR_FILENO, msg, msg_len);
228
229 // Emergency flush for all registered loggers
230 // Note: We cannot safely lock mutex_ here (not signal-safe)
231 // Instead, we access the loggers directly (risk of race condition,
232 // but in a crash scenario, this is acceptable)
233 signal_manager* mgr = current_instance_.load(std::memory_order_acquire);
234 if (!mgr) {
235 // No instance available, nothing to flush
236 _exit(128 + sig);
237 }
238
239 // Call emergency_flush on each logger
240 // Note: This assumes the logger pointers are still valid
241 // In production, consider using a signal-safe data structure
242 for (auto* log : mgr->loggers_) {
243 if (log) {
244 emergency_flush(log);
245 }
246 }
247
248 // Call original handler (or terminate)
249 switch (sig) {
250 case SIGSEGV:
251 if (mgr->original_sigsegv_ != SIG_DFL &&
252 mgr->original_sigsegv_ != SIG_IGN) {
253 mgr->original_sigsegv_(sig);
254 } else {
255 std::signal(SIGSEGV, SIG_DFL);
256 std::raise(SIGSEGV);
257 }
258 break;
259 case SIGABRT:
260 if (mgr->original_sigabrt_ != SIG_DFL &&
261 mgr->original_sigabrt_ != SIG_IGN) {
262 mgr->original_sigabrt_(sig);
263 } else {
264 std::signal(SIGABRT, SIG_DFL);
265 std::raise(SIGABRT);
266 }
267 break;
268 case SIGTERM:
269 if (mgr->original_sigterm_ != SIG_DFL &&
270 mgr->original_sigterm_ != SIG_IGN) {
271 mgr->original_sigterm_(sig);
272 } else {
273 _exit(128 + sig);
274 }
275 break;
276 case SIGINT:
277 if (mgr->original_sigint_ != SIG_DFL &&
278 mgr->original_sigint_ != SIG_IGN) {
279 mgr->original_sigint_(sig);
280 } else {
281 _exit(128 + sig);
282 }
283 break;
284 default:
285 _exit(128 + sig);
286 }
287 }
288
293 static void emergency_flush(critical_logger_interface* log);
294
297 static std::atomic<signal_manager*> current_instance_;
298
299 mutable std::mutex mutex_;
300 std::set<critical_logger_interface*> loggers_;
301 std::atomic<bool> handlers_installed_{false};
302
303 // Original signal handlers
304 void (*original_sigsegv_)(int) = SIG_DFL;
305 void (*original_sigabrt_)(int) = SIG_DFL;
306 void (*original_sigterm_)(int) = SIG_DFL;
307 void (*original_sigint_)(int) = SIG_DFL;
308};
309
315public:
316 virtual ~critical_logger_interface() = default;
317
324 virtual int get_emergency_fd() const = 0;
325
332 virtual const char* get_emergency_buffer() const = 0;
333
340 virtual size_t get_emergency_buffer_size() const = 0;
341};
342
343// Implementation of emergency_flush (signal-safe)
345 if (!log) {
346 return;
347 }
348
349 // Get file descriptor
350 int fd = log->get_emergency_fd();
351 if (fd < 0) {
352 return;
353 }
354
355 // Get buffer
356 const char* buffer = log->get_emergency_buffer();
357 size_t size = log->get_emergency_buffer_size();
358
359 if (buffer && size > 0) {
360 // Write using signal-safe write() syscall
361 ssize_t written = 0;
362 while (written < static_cast<ssize_t>(size)) {
363 ssize_t n = detail::safe_write(fd, buffer + written, size - written);
364 if (n <= 0) {
365 break; // Error or would block
366 }
367 written += n;
368 }
369
370 // Sync to disk (signal-safe)
372 }
373}
374
375} // namespace kcenon::logger::security
Interface for loggers that support emergency flushing.
virtual int get_emergency_fd() const =0
Get file descriptor for emergency writing.
virtual const char * get_emergency_buffer() const =0
Get emergency buffer pointer.
virtual size_t get_emergency_buffer_size() const =0
Get emergency buffer size.
Manager for safe signal handler installation.
void register_logger(critical_logger_interface *log) override
Register a logger to receive signal notifications.
std::set< critical_logger_interface * > loggers_
signal_manager(signal_manager &&)=delete
void install_handlers()
Install signal handlers.
signal_manager(const signal_manager &)=delete
void uninstall_handlers()
Uninstall signal handlers.
static std::atomic< signal_manager * > current_instance_
static void signal_handler(int sig)
Signal handler (must use only signal-safe functions)
static void emergency_flush(critical_logger_interface *log)
Emergency flush for a logger (signal-safe)
void unregister_logger(critical_logger_interface *log) override
Unregister a logger.
bool are_handlers_installed() const override
Check if signal handlers are installed.
size_t logger_count() const override
Get number of registered loggers.
~signal_manager() override=default
Destructor.
signal_manager & operator=(signal_manager &&)=delete
signal_manager()=default
Default constructor.
signal_manager & operator=(const signal_manager &)=delete
DLL export/import macros for logger_system shared library support.
#define LOGGER_SYSTEM_API
ssize_t safe_write(int fd, const void *buf, size_t count)
Signal-safe write wrapper (cross-platform)
int safe_fsync(int fd)
Signal-safe fsync wrapper (cross-platform)
@ size
Rotate based on file size only.
Interface for signal handler management.