xref: /freebsd/lib/libc/gen/uexterr_format.c (revision 6fd98877de633f5ec6f028e78d5a2d94527d63d0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2025 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software were developed by Konstantin Belousov <kib@FreeBSD.org>
8  * under sponsorship from the FreeBSD Foundation.
9  */
10 
11 #include <sys/param.h>
12 #include <sys/exterr_cat.h>
13 #include <sys/exterrvar.h>
14 #include <exterr.h>
15 #include <stdbool.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 
21 static const char * const cat_to_filenames[] = {
22 #include "exterr_cat_filenames.h"
23 };
24 
25 static const char *
cat_to_filename(int category)26 cat_to_filename(int category)
27 {
28 	if (category < 0 || category >= nitems(cat_to_filenames) ||
29 	    cat_to_filenames[category] == NULL)
30 		return ("unknown");
31 	return (cat_to_filenames[category]);
32 }
33 
34 static const char exterror_verbose_name[] = "EXTERROR_VERBOSE";
35 enum exterr_verbose_state {
36 	EXTERR_VERBOSE_UNKNOWN = 100,
37 	EXTERR_VERBOSE_DEFAULT,
38 	EXTERR_VERBOSE_ALLOW_BRIEF,
39 	EXTERR_VERBOSE_ALLOW_FULL,
40 };
41 static enum exterr_verbose_state exterror_verbose = EXTERR_VERBOSE_UNKNOWN;
42 
43 static void
exterr_verbose_init(void)44 exterr_verbose_init(void)
45 {
46 	const char *v;
47 
48 	/*
49 	 * No need to care about thread-safety, the result is
50 	 * idempotent.
51 	 */
52 	if (exterror_verbose != EXTERR_VERBOSE_UNKNOWN)
53 		return;
54 	if (issetugid()) {
55 		exterror_verbose = EXTERR_VERBOSE_DEFAULT;
56 	} else if ((v = getenv(exterror_verbose_name)) != NULL) {
57 		exterror_verbose = strcmp(v, "brief") == 0 ?
58 		    EXTERR_VERBOSE_ALLOW_BRIEF : EXTERR_VERBOSE_ALLOW_FULL;
59 	} else {
60 		exterror_verbose = EXTERR_VERBOSE_DEFAULT;
61 	}
62 }
63 
64 int
__uexterr_format(const struct uexterror * ue,char * buf,size_t bufsz)65 __uexterr_format(const struct uexterror *ue, char *buf, size_t bufsz)
66 {
67 	bool has_msg;
68 
69 	if (bufsz > UEXTERROR_MAXLEN)
70 		bufsz = UEXTERROR_MAXLEN;
71 	if (ue->error == 0) {
72 		strlcpy(buf, "", bufsz);
73 		return (0);
74 	}
75 	exterr_verbose_init();
76 	has_msg = ue->msg[0] != '\0';
77 
78 	if (has_msg) {
79 		snprintf(buf, bufsz, ue->msg, (uintmax_t)ue->p1,
80 		    (uintmax_t)ue->p2);
81 	} else {
82 		strlcpy(buf, "", bufsz);
83 	}
84 
85 	if (exterror_verbose > EXTERR_VERBOSE_DEFAULT || !has_msg) {
86 		char lbuf[128];
87 
88 #define	SRC_FMT "(src sys/%s:%u)"
89 		if (exterror_verbose == EXTERR_VERBOSE_ALLOW_BRIEF) {
90 			snprintf(lbuf, sizeof(lbuf), SRC_FMT,
91                             cat_to_filename(ue->cat), ue->src_line);
92 		} else if (!has_msg ||
93 		    exterror_verbose == EXTERR_VERBOSE_ALLOW_FULL) {
94 			snprintf(lbuf, sizeof(lbuf),
95 			    "errno %d category %u " SRC_FMT " p1 %#jx p2 %#jx",
96 			    ue->error, ue->cat, cat_to_filename(ue->cat),
97 			    ue->src_line, (uintmax_t)ue->p1, (uintmax_t)ue->p2);
98 		}
99 #undef SRC_FMT
100 		if (has_msg)
101 			strlcat(buf, " ", bufsz);
102 		strlcat(buf, lbuf, bufsz);
103 	}
104 	return (0);
105 }
106