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