From abdf5512c8f50525092035266fc9f610f55b98dd Mon Sep 17 00:00:00 2001 From: Leonardo Grasso Date: Thu, 9 Apr 2026 16:55:35 +0200 Subject: [PATCH] fix(userspace): add portable compat wrappers for gmtime_r, localtime_r, strerror_r The previous commits used gmtime_r/localtime_r (unavailable on Windows) and assumed the GNU strerror_r variant returning char* (only on glibc). This broke macOS, musl, WASM, and Win32 builds. Add userspace/engine/compat.h with portable inline wrappers: - falco_gmtime_r / falco_localtime_r: use gmtime_s/localtime_s on Win32 - falco_strerror_r: returns const char* on all platforms, detecting glibc via __GLIBC__ (not _GNU_SOURCE alone, since musl defines _GNU_SOURCE but provides the XSI variant returning int) Also fixes a pre-existing bug in create_signal_handlers.cpp where the GNU strerror_r return value was incorrectly compared to 0 and the actual error string was discarded. Signed-off-by: Leonardo Grasso --- userspace/engine/compat.h | 59 +++++++++++++++++++ userspace/engine/formats.cpp | 3 +- userspace/engine/logger.cpp | 5 +- .../app/actions/create_signal_handlers.cpp | 7 +-- .../app/actions/print_kernel_version.cpp | 3 +- userspace/falco/app/actions/print_support.cpp | 3 +- userspace/falco/falco_outputs.cpp | 3 +- userspace/falco/outputs_program.cpp | 3 +- userspace/falco/stats_writer.cpp | 13 ++-- 9 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 userspace/engine/compat.h diff --git a/userspace/engine/compat.h b/userspace/engine/compat.h new file mode 100644 index 00000000..c0b5fbe1 --- /dev/null +++ b/userspace/engine/compat.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2025 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include +#include + +// Portable gmtime_r: Windows provides gmtime_s with reversed arg order. +inline struct tm* falco_gmtime_r(const time_t* timer, struct tm* buf) { +#ifdef _WIN32 + return gmtime_s(buf, timer) == 0 ? buf : nullptr; +#else + return gmtime_r(timer, buf); +#endif +} + +// Portable localtime_r: Windows provides localtime_s with reversed arg order. +inline struct tm* falco_localtime_r(const time_t* timer, struct tm* buf) { +#ifdef _WIN32 + return localtime_s(buf, timer) == 0 ? buf : nullptr; +#else + return localtime_r(timer, buf); +#endif +} + +// Portable strerror_r: returns const char* on all platforms. +// +// - glibc with _GNU_SOURCE: returns char* that may point to buf or a static string +// - musl/macOS/WASM (XSI): returns int, always writes to buf +// - Windows: no strerror_r, uses strerror_s instead +// +// We check __GLIBC__ (not _GNU_SOURCE alone) because musl defines _GNU_SOURCE +// but always provides the XSI variant. +inline const char* falco_strerror_r(int errnum, char* buf, size_t len) { +#if defined(__GLIBC__) && defined(_GNU_SOURCE) + return strerror_r(errnum, buf, len); +#elif defined(_WIN32) + strerror_s(buf, len, errnum); + return buf; +#else + strerror_r(errnum, buf, len); + return buf; +#endif +} diff --git a/userspace/engine/formats.cpp b/userspace/engine/formats.cpp index 0fcf89f9..7977d4cc 100644 --- a/userspace/engine/formats.cpp +++ b/userspace/engine/formats.cpp @@ -17,6 +17,7 @@ limitations under the License. #include +#include "compat.h" #include "formats.h" #include "falco_engine.h" @@ -99,7 +100,7 @@ std::string falco_formats::format_event(sinsp_evt *evt, std::string iso8601evttime; struct tm tm_buf; - gmtime_r(&evttime, &tm_buf); + falco_gmtime_r(&evttime, &tm_buf); strftime(time_sec, sizeof(time_sec), "%FT%T", &tm_buf); snprintf(time_ns, sizeof(time_ns), ".%09luZ", evt->get_ts() % 1000000000); iso8601evttime = time_sec; diff --git a/userspace/engine/logger.cpp b/userspace/engine/logger.cpp index ad20d3de..b01c6620 100644 --- a/userspace/engine/logger.cpp +++ b/userspace/engine/logger.cpp @@ -16,6 +16,7 @@ limitations under the License. */ #include +#include "compat.h" #include "logger.h" #include "falco_common.h" @@ -123,13 +124,13 @@ void falco_logger::log(falco_logger::level priority, const std::string&& msg) { if(falco_logger::time_format_iso_8601) { char buf[sizeof "YYYY-MM-DDTHH:MM:SS-0000"]; struct tm gtm; - if(gmtime_r(&result, >m) != NULL && + if(falco_gmtime_r(&result, >m) != NULL && (strftime(buf, sizeof(buf), "%FT%T%z", >m) != 0)) { fprintf(stderr, "%s: %s", buf, copy.c_str()); } } else { struct tm ltm; - localtime_r(&result, <m); + falco_localtime_r(&result, <m); char tstr[std::size("WWW MMM DD HH:mm:ss YYYY")]; std::strftime(std::data(tstr), std::size(tstr), "%a %b %d %H:%M:%S %Y", <m); fprintf(stderr, "%s: %s", tstr, copy.c_str()); diff --git a/userspace/falco/app/actions/create_signal_handlers.cpp b/userspace/falco/app/actions/create_signal_handlers.cpp index 0db70208..b311f3c7 100644 --- a/userspace/falco/app/actions/create_signal_handlers.cpp +++ b/userspace/falco/app/actions/create_signal_handlers.cpp @@ -18,6 +18,7 @@ limitations under the License. #include #include "actions.h" +#include "compat.h" #include "../app.h" #include "../signals.h" @@ -49,12 +50,10 @@ bool create_handler(int sig, void (*func)(int), run_result& ret) { #ifdef __linux__ if(signal(sig, func) == SIG_ERR) { char errbuf[1024]; - if(strerror_r(errno, errbuf, sizeof(errbuf)) != 0) { - snprintf(errbuf, sizeof(errbuf) - 1, "Errno %d", errno); - } + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); ret = run_result::fatal(std::string("Could not create signal handler for ") + - strsignal(sig) + ": " + errbuf); + strsignal(sig) + ": " + errstr); } #endif return ret.success; diff --git a/userspace/falco/app/actions/print_kernel_version.cpp b/userspace/falco/app/actions/print_kernel_version.cpp index c31a2c73..87225f71 100644 --- a/userspace/falco/app/actions/print_kernel_version.cpp +++ b/userspace/falco/app/actions/print_kernel_version.cpp @@ -16,6 +16,7 @@ limitations under the License. */ #include "actions.h" +#include "compat.h" #include "helpers.h" #include "../app.h" #include @@ -38,7 +39,7 @@ falco::app::run_result falco::app::actions::print_kernel_version(const falco::ap // We don't want to fail, we just need to log something int saved_errno = errno; char errbuf[256]; - const char* errstr = strerror_r(saved_errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(saved_errno, errbuf, sizeof(errbuf)); falco_logger::log(falco_logger::level::INFO, "Cannot read under '/proc/version' (err_message: '" + std::string(errstr) + "', err_code: " + diff --git a/userspace/falco/app/actions/print_support.cpp b/userspace/falco/app/actions/print_support.cpp index be0f8509..75e00f01 100644 --- a/userspace/falco/app/actions/print_support.cpp +++ b/userspace/falco/app/actions/print_support.cpp @@ -20,6 +20,7 @@ limitations under the License. #else #include #endif +#include "compat.h" #include #include "actions.h" @@ -96,7 +97,7 @@ falco::app::run_result falco::app::actions::print_support(falco::app::state& s) if(get_sysinfo(support) != 0) { char errbuf[256]; - const char* errstr = strerror_r(errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); return run_result::fatal(std::string("Could not get system info: ") + errstr); } diff --git a/userspace/falco/falco_outputs.cpp b/userspace/falco/falco_outputs.cpp index 8a4a3629..d479f1e4 100644 --- a/userspace/falco/falco_outputs.cpp +++ b/userspace/falco/falco_outputs.cpp @@ -16,6 +16,7 @@ limitations under the License. */ #include "falco_outputs.h" +#include "compat.h" #include "config_falco.h" #include "formats.h" @@ -182,7 +183,7 @@ void falco_outputs::handle_msg(uint64_t ts, std::string iso8601evttime; struct tm tm_buf; - gmtime_r(&evttime, &tm_buf); + falco_gmtime_r(&evttime, &tm_buf); strftime(time_sec, sizeof(time_sec), "%FT%T", &tm_buf); snprintf(time_ns, sizeof(time_ns), ".%09luZ", ts % 1000000000); iso8601evttime = time_sec; diff --git a/userspace/falco/outputs_program.cpp b/userspace/falco/outputs_program.cpp index 855c9440..01503278 100644 --- a/userspace/falco/outputs_program.cpp +++ b/userspace/falco/outputs_program.cpp @@ -16,6 +16,7 @@ limitations under the License. */ #include "outputs_program.h" +#include "compat.h" #include "logger.h" #include #include @@ -27,7 +28,7 @@ void falco::outputs::output_program::open_pfile() { if(m_pfile == nullptr) { char errbuf[256]; - const char* errstr = strerror_r(errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); falco_logger::log(falco_logger::level::ERR, "Failed to open program output: " + m_oc.options["program"] + " (error: " + errstr + ")"); diff --git a/userspace/falco/stats_writer.cpp b/userspace/falco/stats_writer.cpp index 54e54fc2..6a76721f 100644 --- a/userspace/falco/stats_writer.cpp +++ b/userspace/falco/stats_writer.cpp @@ -24,6 +24,7 @@ limitations under the License. #include +#include "compat.h" #include "falco_common.h" #include "stats_writer.h" #include "logger.h" @@ -72,7 +73,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { handler.sa_handler = &timer_handler; if(sigaction(SIGALRM, &handler, NULL) == -1) { char errbuf[256]; - const char* errstr = strerror_r(errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); err = std::string("Could not set up signal handler for periodic timer: ") + errstr; return false; } @@ -96,7 +97,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { handler.sa_handler = &timer_handler; if(sigaction(SIGALRM, &handler, NULL) == -1) { char errbuf[256]; - const char* errstr = strerror_r(errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); err = std::string("Could not set up signal handler for periodic timer: ") + errstr; return false; } @@ -124,7 +125,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { handler.sa_handler = &timer_handler; if(sigaction(SIGALRM, &handler, NULL) == -1) { char errbuf[256]; - const char* errstr = strerror_r(errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); err = std::string("Could not set up signal handler for periodic timer: ") + errstr; return false; } @@ -138,7 +139,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { if(s_timerid_exists) { if(timer_delete(s_timerid) == -1) { char errbuf[256]; - const char* errstr = strerror_r(errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); err = std::string("Could not delete previous timer: ") + errstr; return false; } @@ -147,7 +148,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { if(timer_create(CLOCK_MONOTONIC, &sev, &s_timerid) == -1) { char errbuf[256]; - const char* errstr = strerror_r(errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); err = std::string("Could not create periodic timer: ") + errstr; return false; } @@ -159,7 +160,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { if(timer_settime(s_timerid, 0, &timer, NULL) == -1) { char errbuf[256]; - const char* errstr = strerror_r(errno, errbuf, sizeof(errbuf)); + const char* errstr = falco_strerror_r(errno, errbuf, sizeof(errbuf)); err = std::string("Could not set up periodic timer: ") + errstr; return false; }