1*3fe5961aSDevin Teske /*
2*3fe5961aSDevin Teske * Copyright (c) 2013-2026 Devin Teske <dteske@FreeBSD.org>
3*3fe5961aSDevin Teske * Copyright (c) 2021-2026 Faraz Vahedi <kfv@FreeBSD.org>
4*3fe5961aSDevin Teske *
5*3fe5961aSDevin Teske * SPDX-License-Identifier: BSD-2-Clause
6*3fe5961aSDevin Teske */
7*3fe5961aSDevin Teske
8*3fe5961aSDevin Teske /*
9*3fe5961aSDevin Teske * Directive descriptions harvested from defaults-file comments (-d).
10*3fe5961aSDevin Teske */
11*3fe5961aSDevin Teske
12*3fe5961aSDevin Teske #include "sysconf_priv.h"
13*3fe5961aSDevin Teske
14*3fe5961aSDevin Teske /*
15*3fe5961aSDevin Teske * Directive descriptions harvested from the defaults file by
16*3fe5961aSDevin Teske * load_descriptions() below.
17*3fe5961aSDevin Teske */
18*3fe5961aSDevin Teske struct descent {
19*3fe5961aSDevin Teske char *name; /* directive name */
20*3fe5961aSDevin Teske char *desc; /* description (may be empty) */
21*3fe5961aSDevin Teske };
22*3fe5961aSDevin Teske
23*3fe5961aSDevin Teske static struct descent *descs; /* set by load_descriptions() */
24*3fe5961aSDevin Teske static size_t ndescs;
25*3fe5961aSDevin Teske static size_t descsize;
26*3fe5961aSDevin Teske
27*3fe5961aSDevin Teske /*
28*3fe5961aSDevin Teske * Harvest every directive description from the defaults file, where a
29*3fe5961aSDevin Teske * description is the inline comment trailing the directive's default
30*3fe5961aSDevin Teske * assignment, continued across any subsequent lines of whitespace
31*3fe5961aSDevin Teske * followed by a comment character. A commented-out default
32*3fe5961aSDevin Teske * (`#comconsole_speed="115200" # Set the ...') yields its directive and
33*3fe5961aSDevin Teske * description all the same -- deliberately unlike sysrc(8), which goes
34*3fe5961aSDevin Teske * blind when the default itself is disabled. Comment prose is never
35*3fe5961aSDevin Teske * mistaken for a directive: a name must directly abut the optional
36*3fe5961aSDevin Teske * leading `#' and run unbroken to its `='.
37*3fe5961aSDevin Teske */
38*3fe5961aSDevin Teske void
load_descriptions(void)39*3fe5961aSDevin Teske load_descriptions(void)
40*3fe5961aSDevin Teske {
41*3fe5961aSDevin Teske int current = -1; /* entry whose description is growing */
42*3fe5961aSDevin Teske FILE *fp;
43*3fe5961aSDevin Teske ssize_t linelen;
44*3fe5961aSDevin Teske size_t len;
45*3fe5961aSDevin Teske size_t linesize = 0;
46*3fe5961aSDevin Teske size_t n;
47*3fe5961aSDevin Teske char *defaults;
48*3fe5961aSDevin Teske char *line = NULL;
49*3fe5961aSDevin Teske char *p;
50*3fe5961aSDevin Teske char *v;
51*3fe5961aSDevin Teske struct descent *tmp;
52*3fe5961aSDevin Teske
53*3fe5961aSDevin Teske if ((defaults = defaults_path()) == NULL)
54*3fe5961aSDevin Teske errx(EXIT_FAILURE, "target has no defaults file");
55*3fe5961aSDevin Teske if ((fp = fopen(defaults, "r")) == NULL)
56*3fe5961aSDevin Teske err(EXIT_FAILURE, "%s", defaults);
57*3fe5961aSDevin Teske
58*3fe5961aSDevin Teske while ((linelen = getline(&line, &linesize, fp)) != -1) {
59*3fe5961aSDevin Teske int quoted = 0;
60*3fe5961aSDevin Teske
61*3fe5961aSDevin Teske if (linelen > 0 && line[linelen - 1] == '\n')
62*3fe5961aSDevin Teske line[--linelen] = '\0';
63*3fe5961aSDevin Teske
64*3fe5961aSDevin Teske /* Whitespace then `#' continues the open description */
65*3fe5961aSDevin Teske p = line;
66*3fe5961aSDevin Teske if (*p == ' ' || *p == '\t') {
67*3fe5961aSDevin Teske p += strspn(p, " \t");
68*3fe5961aSDevin Teske if (*p == '#' && current >= 0) {
69*3fe5961aSDevin Teske char *grown;
70*3fe5961aSDevin Teske
71*3fe5961aSDevin Teske p++;
72*3fe5961aSDevin Teske p += strspn(p, " \t");
73*3fe5961aSDevin Teske if (*p == '\0')
74*3fe5961aSDevin Teske continue;
75*3fe5961aSDevin Teske len = strlen(descs[current].desc) +
76*3fe5961aSDevin Teske strlen(p) + 2;
77*3fe5961aSDevin Teske if ((grown = malloc(len)) == NULL)
78*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
79*3fe5961aSDevin Teske snprintf(grown, len, "%s%s%s",
80*3fe5961aSDevin Teske descs[current].desc,
81*3fe5961aSDevin Teske *descs[current].desc != '\0' ? " " : "",
82*3fe5961aSDevin Teske p);
83*3fe5961aSDevin Teske free(descs[current].desc);
84*3fe5961aSDevin Teske descs[current].desc = grown;
85*3fe5961aSDevin Teske continue;
86*3fe5961aSDevin Teske }
87*3fe5961aSDevin Teske current = -1;
88*3fe5961aSDevin Teske continue;
89*3fe5961aSDevin Teske }
90*3fe5961aSDevin Teske current = -1;
91*3fe5961aSDevin Teske
92*3fe5961aSDevin Teske /* A commented-out default still describes its directive */
93*3fe5961aSDevin Teske if (*p == '#')
94*3fe5961aSDevin Teske p++;
95*3fe5961aSDevin Teske
96*3fe5961aSDevin Teske /* A directive name runs unbroken to its `=' */
97*3fe5961aSDevin Teske len = strcspn(p, " \t\"#=");
98*3fe5961aSDevin Teske if (len == 0 || p[len] != '=')
99*3fe5961aSDevin Teske continue;
100*3fe5961aSDevin Teske
101*3fe5961aSDevin Teske /* First description wins on a repeated directive */
102*3fe5961aSDevin Teske for (n = 0; n < ndescs; n++)
103*3fe5961aSDevin Teske if (strncmp(descs[n].name, p, len) == 0 &&
104*3fe5961aSDevin Teske descs[n].name[len] == '\0')
105*3fe5961aSDevin Teske break;
106*3fe5961aSDevin Teske if (n < ndescs)
107*3fe5961aSDevin Teske continue;
108*3fe5961aSDevin Teske
109*3fe5961aSDevin Teske /* Skip the value (quotes may hide a `#') */
110*3fe5961aSDevin Teske for (v = p + len + 1; *v != '\0'; v++) {
111*3fe5961aSDevin Teske if (*v == '"')
112*3fe5961aSDevin Teske quoted = !quoted;
113*3fe5961aSDevin Teske else if (*v == '#' && !quoted)
114*3fe5961aSDevin Teske break;
115*3fe5961aSDevin Teske }
116*3fe5961aSDevin Teske if (*v == '#') {
117*3fe5961aSDevin Teske v++;
118*3fe5961aSDevin Teske v += strspn(v, " \t");
119*3fe5961aSDevin Teske }
120*3fe5961aSDevin Teske
121*3fe5961aSDevin Teske if (ndescs >= descsize) {
122*3fe5961aSDevin Teske descsize = (descsize == 0) ? 64 : descsize << 1;
123*3fe5961aSDevin Teske tmp = realloc(descs, descsize * sizeof(*descs));
124*3fe5961aSDevin Teske if (tmp == NULL)
125*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
126*3fe5961aSDevin Teske descs = tmp;
127*3fe5961aSDevin Teske }
128*3fe5961aSDevin Teske descs[ndescs].name = strndup(p, len);
129*3fe5961aSDevin Teske descs[ndescs].desc = strdup(v);
130*3fe5961aSDevin Teske if (descs[ndescs].name == NULL ||
131*3fe5961aSDevin Teske descs[ndescs].desc == NULL)
132*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
133*3fe5961aSDevin Teske current = (int)ndescs;
134*3fe5961aSDevin Teske ndescs++;
135*3fe5961aSDevin Teske }
136*3fe5961aSDevin Teske free(line);
137*3fe5961aSDevin Teske fclose(fp);
138*3fe5961aSDevin Teske free(defaults);
139*3fe5961aSDevin Teske }
140*3fe5961aSDevin Teske
141*3fe5961aSDevin Teske /*
142*3fe5961aSDevin Teske * Return the harvested description of `name' or NULL if the defaults file
143*3fe5961aSDevin Teske * does not mention it.
144*3fe5961aSDevin Teske */
145*3fe5961aSDevin Teske static const char *
desc_find(const char * name)146*3fe5961aSDevin Teske desc_find(const char *name)
147*3fe5961aSDevin Teske {
148*3fe5961aSDevin Teske size_t n;
149*3fe5961aSDevin Teske
150*3fe5961aSDevin Teske for (n = 0; n < ndescs; n++)
151*3fe5961aSDevin Teske if (strcmp(descs[n].name, name) == 0)
152*3fe5961aSDevin Teske return (descs[n].desc);
153*3fe5961aSDevin Teske return (NULL);
154*3fe5961aSDevin Teske }
155*3fe5961aSDevin Teske
156*3fe5961aSDevin Teske /*
157*3fe5961aSDevin Teske * Print the description of `directive' according to the display flags: for
158*3fe5961aSDevin Teske * the sysctl target, from the running kernel; otherwise from the table
159*3fe5961aSDevin Teske * harvested off the defaults file (a directive the defaults never mention
160*3fe5961aSDevin Teske * prints an empty description).
161*3fe5961aSDevin Teske */
162*3fe5961aSDevin Teske void
print_desc(const char * directive)163*3fe5961aSDevin Teske print_desc(const char *directive)
164*3fe5961aSDevin Teske {
165*3fe5961aSDevin Teske const char *desc = NULL;
166*3fe5961aSDevin Teske #if defined(__FreeBSD__)
167*3fe5961aSDevin Teske char buf[BUFSIZ];
168*3fe5961aSDevin Teske
169*3fe5961aSDevin Teske if (format == BSDCONF_FORMAT_SYSCTL) {
170*3fe5961aSDevin Teske (void)sysctl_descr(directive, buf, sizeof(buf));
171*3fe5961aSDevin Teske desc = buf;
172*3fe5961aSDevin Teske }
173*3fe5961aSDevin Teske #endif
174*3fe5961aSDevin Teske if (desc == NULL && (desc = desc_find(directive)) == NULL)
175*3fe5961aSDevin Teske desc = "";
176*3fe5961aSDevin Teske if (name_only)
177*3fe5961aSDevin Teske puts(directive);
178*3fe5961aSDevin Teske else if (value_only)
179*3fe5961aSDevin Teske puts(desc);
180*3fe5961aSDevin Teske else
181*3fe5961aSDevin Teske printf("%s: %s\n", directive, desc);
182*3fe5961aSDevin Teske }
183*3fe5961aSDevin Teske
184*3fe5961aSDevin Teske /*
185*3fe5961aSDevin Teske * Process `-d' with explicit names for a defaults-backed target. Returns
186*3fe5961aSDevin Teske * EXIT_SUCCESS or EXIT_FAILURE.
187*3fe5961aSDevin Teske */
188*3fe5961aSDevin Teske int
describe_defaults(void)189*3fe5961aSDevin Teske describe_defaults(void)
190*3fe5961aSDevin Teske {
191*3fe5961aSDevin Teske int rv = EXIT_SUCCESS;
192*3fe5961aSDevin Teske unsigned int n;
193*3fe5961aSDevin Teske
194*3fe5961aSDevin Teske load_descriptions();
195*3fe5961aSDevin Teske for (n = 0; n < nreqs; n++) {
196*3fe5961aSDevin Teske if (desc_find(reqs[n].name) == NULL) {
197*3fe5961aSDevin Teske if (ignore_unknown)
198*3fe5961aSDevin Teske continue;
199*3fe5961aSDevin Teske if (!quiet)
200*3fe5961aSDevin Teske warnx("unknown directive '%s'",
201*3fe5961aSDevin Teske reqs[n].name);
202*3fe5961aSDevin Teske rv = EXIT_FAILURE;
203*3fe5961aSDevin Teske continue;
204*3fe5961aSDevin Teske }
205*3fe5961aSDevin Teske print_desc(reqs[n].name);
206*3fe5961aSDevin Teske }
207*3fe5961aSDevin Teske
208*3fe5961aSDevin Teske return (rv);
209*3fe5961aSDevin Teske }
210