xref: /freebsd/usr.sbin/sysconf/sysconf_query_desc.c (revision 3fe5961a0b708da599d42cbb6b5e4f030c28ea45)
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