git: 42210fe8dcd4 - main - exterror: Add EXTERROR_VERBOSE env variable to control verbosity

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Mon, 29 Dec 2025 01:16:34 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=42210fe8dcd4684a45e0e4db1b8194e291dc8fda

commit 42210fe8dcd4684a45e0e4db1b8194e291dc8fda
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2025-12-28 00:23:27 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2025-12-29 01:16:25 +0000

    exterror: Add EXTERROR_VERBOSE env variable to control verbosity
    
    If the variable is set and the process is not suid, __uexterr_format(),
    used by err(3), prints errno/category/source line/pX always, not only
    when there is no kernel message provided.
    
    Requested by:   mckusick
    Reviewed by:    emaste, mckusick
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D54380
---
 lib/libc/gen/uexterr_format.c | 53 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 48 insertions(+), 5 deletions(-)

diff --git a/lib/libc/gen/uexterr_format.c b/lib/libc/gen/uexterr_format.c
index 68cd2abfe312..3321fd80616d 100644
--- a/lib/libc/gen/uexterr_format.c
+++ b/lib/libc/gen/uexterr_format.c
@@ -11,26 +11,69 @@
 #include <sys/types.h>
 #include <sys/exterrvar.h>
 #include <exterr.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+
+static const char exterror_verbose_name[] = "EXTERROR_VERBOSE";
+enum exterr_verbose_state {
+	EXTERR_VERBOSE_UNKNOWN = 100,
+	EXTERR_VERBOSE_DEFAULT,
+	EXTERR_VERBOSE_ALLOW,
+};
+static enum exterr_verbose_state exterror_verbose = EXTERR_VERBOSE_UNKNOWN;
+
+static void
+exterr_verbose_init(void)
+{
+	/*
+	 * No need to care about thread-safety, the result is
+	 * idempotent.
+	 */
+	if (exterror_verbose != EXTERR_VERBOSE_UNKNOWN)
+		return;
+	if (issetugid()) {
+		exterror_verbose = EXTERR_VERBOSE_DEFAULT;
+	} else if (getenv(exterror_verbose_name) != NULL) {
+		exterror_verbose = EXTERR_VERBOSE_ALLOW;
+	} else {
+		exterror_verbose = EXTERR_VERBOSE_DEFAULT;
+	}
+}
 
 int
 __uexterr_format(const struct uexterror *ue, char *buf, size_t bufsz)
 {
+	bool has_msg;
+
 	if (bufsz > UEXTERROR_MAXLEN)
 		bufsz = UEXTERROR_MAXLEN;
 	if (ue->error == 0) {
 		strlcpy(buf, "", bufsz);
 		return (0);
 	}
-	if (ue->msg[0] == '\0') {
-		snprintf(buf, bufsz,
+	exterr_verbose_init();
+	has_msg = ue->msg[0] != '\0';
+
+	if (has_msg) {
+		snprintf(buf, bufsz, ue->msg, (uintmax_t)ue->p1,
+		    (uintmax_t)ue->p2);
+	} else {
+		strlcpy(buf, "", bufsz);
+	}
+
+	if (exterror_verbose == EXTERR_VERBOSE_ALLOW || !has_msg) {
+		char lbuf[128];
+
+		snprintf(lbuf, sizeof(lbuf),
 		    "errno %d category %u (src line %u) p1 %#jx p2 %#jx",
 		    ue->error, ue->cat, ue->src_line,
 		    (uintmax_t)ue->p1, (uintmax_t)ue->p2);
-	} else {
-		snprintf(buf, bufsz, ue->msg, (uintmax_t)ue->p1,
-		    (uintmax_t)ue->p2);
+		if (has_msg)
+			strlcat(buf, " ", bufsz);
+		strlcat(buf, lbuf, bufsz);
 	}
 	return (0);
 }