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