This is an automated email from the ASF dual-hosted git repository.
bryancall pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 4b6f790351 Fix incorrect errno short names on FreeBSD/macOS in
bwf::Errno (#13240)
4b6f790351 is described below
commit 4b6f79035141c3904c889ff83f0b68dff5209f53
Author: Bryan Call <[email protected]>
AuthorDate: Wed Jun 17 08:23:16 2026 -0700
Fix incorrect errno short names on FreeBSD/macOS in bwf::Errno (#13240)
bwf::Errno used a single Linux-numbered table indexed by raw errno, so
other platforms printed the wrong name (e.g. ETIMEDOUT=60 logged as
ENOSTR on FreeBSD/macOS). Replace it with a per-platform table built
from each OS's <errno.h>, add guarded tests, and #error on unsupported
platforms.
---
lib/swoc/src/bw_format.cc | 155 +++++++++++++++++-----------------
lib/swoc/unit_tests/test_bw_format.cc | 35 ++++++++
2 files changed, 111 insertions(+), 79 deletions(-)
diff --git a/lib/swoc/src/bw_format.cc b/lib/swoc/src/bw_format.cc
index 5b87fc9909..28a2348d4d 100644
--- a/lib/swoc/src/bw_format.cc
+++ b/lib/swoc/src/bw_format.cc
@@ -743,83 +743,79 @@ FixedBufferWriter::operator>>(std::ostream &s) const {
}
namespace {
-// Hand rolled, might not be totally compliant everywhere, but probably close
-// enough. The long string will be locally accurate. Clang requires the double
-// braces. Why, Turing only knows.
-static const std::array<std::string_view, 134> ERRNO_SHORT_NAME = {
- {
- "SUCCESS", "EPERM",
- "ENOENT", "ESRCH",
- "EINTR", "EIO",
- "ENXIO", "E2BIG ",
- "ENOEXEC", "EBADF",
- "ECHILD", "EAGAIN",
- "ENOMEM", "EACCES",
- "EFAULT", "ENOTBLK",
- "EBUSY", "EEXIST",
- "EXDEV", "ENODEV",
- "ENOTDIR", "EISDIR",
- "EINVAL", "ENFILE",
- "EMFILE", "ENOTTY",
- "ETXTBSY", "EFBIG",
- "ENOSPC", "ESPIPE",
- "EROFS", "EMLINK",
- "EPIPE", "EDOM",
- "ERANGE", "EDEADLK",
- "ENAMETOOLONG", "ENOLCK",
- "ENOSYS", "ENOTEMPTY",
- "ELOOP", "EWOULDBLOCK",
- "ENOMSG", "EIDRM",
- "ECHRNG", "EL2NSYNC",
- "EL3HLT", "EL3RST",
- "ELNRNG", "EUNATCH",
- "ENOCSI", "EL2HTL",
- "EBADE", "EBADR",
- "EXFULL", "ENOANO",
- "EBADRQC", "EBADSLT",
- "EDEADLOCK", "EBFONT",
- "ENOSTR", "ENODATA",
- "ETIME", "ENOSR",
- "ENONET", "ENOPKG",
- "EREMOTE", "ENOLINK",
- "EADV", "ESRMNT",
- "ECOMM", "EPROTO",
- "EMULTIHOP", "EDOTDOT",
- "EBADMSG", "EOVERFLOW",
- "ENOTUNIQ", "EBADFD",
- "EREMCHG", "ELIBACC",
- "ELIBBAD", "ELIBSCN",
- "ELIBMAX", "ELIBEXEC",
- "EILSEQ", "ERESTART",
- "ESTRPIPE", "EUSERS",
- "ENOTSOCK", "EDESTADDRREQ",
- "EMSGSIZE", "EPROTOTYPE",
- "ENOPROTOOPT", "EPROTONOSUPPORT",
- "ESOCKTNOSUPPORT", "EOPNOTSUPP",
- "EPFNOSUPPORT", "EAFNOSUPPORT",
- "EADDRINUSE", "EADDRNOTAVAIL",
- "ENETDOWN", "ENETUNREACH",
- "ENETRESET", "ECONNABORTED",
- "ECONNRESET", "ENOBUFS",
- "EISCONN", "ENOTCONN",
- "ESHUTDOWN", "ETOOMANYREFS",
- "ETIMEDOUT", "ECONNREFUSED",
- "EHOSTDOWN", "EHOSTUNREACH",
- "EALREADY", "EINPROGRESS",
- "ESTALE", "EUCLEAN",
- "ENOTNAM", "ENAVAIL",
- "EISNAM", "EREMOTEIO",
- "EDQUOT", "ENOMEDIUM",
- "EMEDIUMTYPE", "ECANCELED",
- "ENOKEY", "EKEYEXPIRED",
- "EKEYREVOKED", "EKEYREJECTED",
- "EOWNERDEAD", "ENOTRECOVERABLE",
- "ERFKILL", "EHWPOISON",
- }
-};
-static constexpr DiscreteRange<unsigned> ERRNO_RANGE{0,
ERRNO_SHORT_NAME.size() - 1};
-// This provides convenient safe access to the errno short name array.
-auto errno_short_name = [](unsigned n) { return ERRNO_RANGE.contains(n) ?
ERRNO_SHORT_NAME[n] : "Unknown"sv; };
+// errno value -> symbolic name, indexed directly by the errno number (O(1)).
+// The numbering differs between Linux and the BSD-derived platforms, and even
+// macOS and FreeBSD disagree on a few of their own high-numbered codes, so
each
+// platform carries a table generated from its real <errno.h>. Gaps in the
+// numbering are empty and fall through to "Unknown"; a platform with no table
+// is a compile error. The long form (the 'l' spec, below) still uses
strerror().
+// clang-format off
+#if defined(__linux__)
+static constexpr std::array<std::string_view, 134> ERRNO_NAMES = {{
+ /* 0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO",
"E2BIG",
+ /* 8 */ "ENOEXEC", "EBADF", "ECHILD", "EAGAIN", "ENOMEM", "EACCES",
"EFAULT", "ENOTBLK",
+ /* 16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR",
"EINVAL", "ENFILE",
+ /* 24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE",
"EROFS", "EMLINK",
+ /* 32 */ "EPIPE", "EDOM", "ERANGE", "EDEADLK", "ENAMETOOLONG", "ENOLCK",
"ENOSYS", "ENOTEMPTY",
+ /* 40 */ "ELOOP", "", "ENOMSG", "EIDRM", "ECHRNG", "EL2NSYNC", "EL3HLT",
"EL3RST",
+ /* 48 */ "ELNRNG", "EUNATCH", "ENOCSI", "EL2HLT", "EBADE", "EBADR",
"EXFULL", "ENOANO",
+ /* 56 */ "EBADRQC", "EBADSLT", "", "EBFONT", "ENOSTR", "ENODATA", "ETIME",
"ENOSR",
+ /* 64 */ "ENONET", "ENOPKG", "EREMOTE", "ENOLINK", "EADV", "ESRMNT",
"ECOMM", "EPROTO",
+ /* 72 */ "EMULTIHOP", "EDOTDOT", "EBADMSG", "EOVERFLOW", "ENOTUNIQ",
"EBADFD", "EREMCHG", "ELIBACC",
+ /* 80 */ "ELIBBAD", "ELIBSCN", "ELIBMAX", "ELIBEXEC", "EILSEQ", "ERESTART",
"ESTRPIPE", "EUSERS",
+ /* 88 */ "ENOTSOCK", "EDESTADDRREQ", "EMSGSIZE", "EPROTOTYPE",
"ENOPROTOOPT", "EPROTONOSUPPORT", "ESOCKTNOSUPPORT", "EOPNOTSUPP",
+ /* 96 */ "EPFNOSUPPORT", "EAFNOSUPPORT", "EADDRINUSE", "EADDRNOTAVAIL",
"ENETDOWN", "ENETUNREACH", "ENETRESET", "ECONNABORTED",
+ /* 104 */ "ECONNRESET", "ENOBUFS", "EISCONN", "ENOTCONN", "ESHUTDOWN",
"ETOOMANYREFS", "ETIMEDOUT", "ECONNREFUSED",
+ /* 112 */ "EHOSTDOWN", "EHOSTUNREACH", "EALREADY", "EINPROGRESS", "ESTALE",
"EUCLEAN", "ENOTNAM", "ENAVAIL",
+ /* 120 */ "EISNAM", "EREMOTEIO", "EDQUOT", "ENOMEDIUM", "EMEDIUMTYPE",
"ECANCELED", "ENOKEY", "EKEYEXPIRED",
+ /* 128 */ "EKEYREVOKED", "EKEYREJECTED", "EOWNERDEAD", "ENOTRECOVERABLE",
"ERFKILL", "EHWPOISON",
+}};
+#elif defined(__APPLE__)
+static constexpr std::array<std::string_view, 108> ERRNO_NAMES = {{
+ /* 0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO",
"E2BIG",
+ /* 8 */ "ENOEXEC", "EBADF", "ECHILD", "EDEADLK", "ENOMEM", "EACCES",
"EFAULT", "ENOTBLK",
+ /* 16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR",
"EINVAL", "ENFILE",
+ /* 24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE",
"EROFS", "EMLINK",
+ /* 32 */ "EPIPE", "EDOM", "ERANGE", "EAGAIN", "EINPROGRESS", "EALREADY",
"ENOTSOCK", "EDESTADDRREQ",
+ /* 40 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", "EPROTONOSUPPORT",
"ESOCKTNOSUPPORT", "ENOTSUP", "EPFNOSUPPORT", "EAFNOSUPPORT",
+ /* 48 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH",
"ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
+ /* 56 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT",
"ECONNREFUSED", "ELOOP", "ENAMETOOLONG",
+ /* 64 */ "EHOSTDOWN", "EHOSTUNREACH", "ENOTEMPTY", "EPROCLIM", "EUSERS",
"EDQUOT", "ESTALE", "EREMOTE",
+ /* 72 */ "EBADRPC", "ERPCMISMATCH", "EPROGUNAVAIL", "EPROGMISMATCH",
"EPROCUNAVAIL", "ENOLCK", "ENOSYS", "EFTYPE",
+ /* 80 */ "EAUTH", "ENEEDAUTH", "EPWROFF", "EDEVERR", "EOVERFLOW",
"EBADEXEC", "EBADARCH", "ESHLIBVERS",
+ /* 88 */ "EBADMACHO", "ECANCELED", "EIDRM", "ENOMSG", "EILSEQ", "ENOATTR",
"EBADMSG", "EMULTIHOP",
+ /* 96 */ "ENODATA", "ENOLINK", "ENOSR", "ENOSTR", "EPROTO", "ETIME",
"EOPNOTSUPP", "ENOPOLICY",
+ /* 104 */ "ENOTRECOVERABLE", "EOWNERDEAD", "EQFULL", "ENOTCAPABLE",
+}};
+#elif defined(__FreeBSD__)
+static constexpr std::array<std::string_view, 98> ERRNO_NAMES = {{
+ /* 0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO",
"E2BIG",
+ /* 8 */ "ENOEXEC", "EBADF", "ECHILD", "EDEADLK", "ENOMEM", "EACCES",
"EFAULT", "ENOTBLK",
+ /* 16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR",
"EINVAL", "ENFILE",
+ /* 24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE",
"EROFS", "EMLINK",
+ /* 32 */ "EPIPE", "EDOM", "ERANGE", "EAGAIN", "EINPROGRESS", "EALREADY",
"ENOTSOCK", "EDESTADDRREQ",
+ /* 40 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", "EPROTONOSUPPORT",
"ESOCKTNOSUPPORT", "EOPNOTSUPP", "EPFNOSUPPORT", "EAFNOSUPPORT",
+ /* 48 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH",
"ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
+ /* 56 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT",
"ECONNREFUSED", "ELOOP", "ENAMETOOLONG",
+ /* 64 */ "EHOSTDOWN", "EHOSTUNREACH", "ENOTEMPTY", "EPROCLIM", "EUSERS",
"EDQUOT", "ESTALE", "EREMOTE",
+ /* 72 */ "EBADRPC", "ERPCMISMATCH", "EPROGUNAVAIL", "EPROGMISMATCH",
"EPROCUNAVAIL", "ENOLCK", "ENOSYS", "EFTYPE",
+ /* 80 */ "EAUTH", "ENEEDAUTH", "EIDRM", "ENOMSG", "EOVERFLOW", "ECANCELED",
"EILSEQ", "ENOATTR",
+ /* 88 */ "EDOOFUS", "EBADMSG", "EMULTIHOP", "ENOLINK", "EPROTO",
"ENOTCAPABLE", "ECAPMODE", "ENOTRECOVERABLE",
+ /* 96 */ "EOWNERDEAD", "EINTEGRITY",
+}};
+#else
+#error "errno name table not defined for this platform"
+#endif
+// clang-format on
+
+std::string_view
+errno_short_name(int e)
+{
+ if (e >= 0 && e < static_cast<int>(ERRNO_NAMES.size()) &&
!ERRNO_NAMES[e].empty()) {
+ return ERRNO_NAMES[e];
+ }
+ return "Unknown"sv;
+}
} // namespace
BufferWriter &
@@ -910,8 +906,9 @@ bwformat(BufferWriter &w, bwf::Spec const &spec,
std::error_code const &ec) {
if (spec.has_numeric_type()) { // if numeric type,
print just the numeric part.
bwformat(w, spec, ec.value());
} else {
- if ((&ec.category() == G_CAT || &ec.category() == S_CAT) &&
swoc::ERRNO_RANGE.contains(ec.value())) {
- bwformat(w, spec, swoc::ERRNO_SHORT_NAME[ec.value()]);
+ auto short_name = (&ec.category() == G_CAT || &ec.category() == S_CAT) ?
errno_short_name(ec.value()) : "Unknown"sv;
+ if (short_name != "Unknown"sv) {
+ bwformat(w, spec, short_name);
} else {
w.write(ec.message());
}
diff --git a/lib/swoc/unit_tests/test_bw_format.cc
b/lib/swoc/unit_tests/test_bw_format.cc
index 2cfb99ad2f..caf69a0499 100644
--- a/lib/swoc/unit_tests/test_bw_format.cc
+++ b/lib/swoc/unit_tests/test_bw_format.cc
@@ -8,6 +8,9 @@
#include <iostream>
#include <variant>
#include <cmath>
+#include <cerrno>
+#include <string>
+#include <system_error>
#include <netinet/in.h>
@@ -545,6 +548,38 @@ TEST_CASE("bwstring std formats", "[libswoc][bwprint]") {
w.clear().print("{::l}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "Permission denied [13]"sv);
+ // The symbolic short name must come from the running platform's <errno.h>,
+ // not a fixed Linux-numbered table. Regression guard for the FreeBSD/macOS
+ // mislabel where ETIMEDOUT (errno 60 on those platforms) printed as "ENOSTR"
+ // (apache/trafficserver#13203). Use the macros so each platform checks its
+ // own numbering.
+ w.clear().print("{:s:s}", swoc::bwf::Errno(EPERM));
+ REQUIRE(w.view() == "EPERM"sv);
+ w.clear().print("{:s:s}", swoc::bwf::Errno(ETIMEDOUT));
+ REQUIRE(w.view() == "ETIMEDOUT"sv);
+ w.clear().print("{:s:s}", swoc::bwf::Errno(ECONNREFUSED));
+ REQUIRE(w.view() == "ECONNREFUSED"sv);
+
+ // Direct guards for the codes that were missing from the tables (each one
+ // formatted as "Unknown" before this fix). They are platform-specific, so
+ // each only compiles where it is defined: ENOATTR on macOS/FreeBSD, EL2HLT
+ // on Linux.
+#ifdef ENOATTR
+ w.clear().print("{:s:s}", swoc::bwf::Errno(ENOATTR));
+ REQUIRE(w.view() == "ENOATTR"sv);
+#endif
+#ifdef EL2HLT
+ w.clear().print("{:s:s}", swoc::bwf::Errno(EL2HLT));
+ REQUIRE(w.view() == "EL2HLT"sv);
+#endif
+
+ // The std::error_code formatter routes through the same per-platform table,
+ // rendering "<name> [<value>]". Build the expectation from the macro so the
+ // numeric part matches the running platform's numbering.
+ std::string ec_expected = "ECONNREFUSED [" + std::to_string(ECONNREFUSED) +
"]";
+ w.clear().print("{}", std::error_code(ECONNREFUSED,
std::generic_category()));
+ REQUIRE(w.view() == ec_expected);
+
time_t t = 1528484137;
// default is GMT
w.clear().print("{} is {}", t, swoc::bwf::Date(t));