1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * * 20 ***********************************************************************/ 21 #pragma prototyped 22 /* 23 * Glenn Fowler 24 * AT&T Research 25 * 26 * getconf - get configuration values 27 */ 28 29 static const char usage[] = 30 "[-?\n@(#)$Id: getconf (AT&T Research) 2008-04-24 $\n]" 31 USAGE_LICENSE 32 "[+NAME?getconf - get configuration values]" 33 "[+DESCRIPTION?\bgetconf\b displays the system configuration value for" 34 " \aname\a. If \aname\a is a filesystem specific variable then" 35 " the value is determined relative to \apath\a or the current" 36 " directory if \apath\a is omitted. If \avalue\a is specified then" 37 " \bgetconf\b attempts to change the process local value to \avalue\a." 38 " \b-\b may be used in place of \apath\a when it is not relevant." 39 " If \apath\a is \b=\b then the the \avalue\a is cached and used" 40 " for subsequent tests in the calling and all child processes." 41 " Only \bwritable\b variables may be set; \breadonly\b variables" 42 " cannot be changed.]" 43 "[+?The current value for \aname\a is written to the standard output. If" 44 " \aname\a is valid but undefined then \bundefined\b is written to" 45 " the standard output. If \aname\a is invalid or an error occurs in" 46 " determining its value, then a diagnostic written to the standard error" 47 " and \bgetconf\b exits with a non-zero exit status.]" 48 "[+?More than one variable may be set or queried by providing the \aname\a" 49 " \apath\a \avalue\a 3-tuple for each variable, specifying \b-\b for" 50 " \avalue\a when querying.]" 51 "[+?If no operands are specified then all known variables are written in" 52 " \aname\a=\avalue\a form to the standard output, one per line." 53 " Only one of \b--call\b, \b--name\b or \b--standard\b may be specified.]" 54 "[+?This implementation uses the \bastgetconf\b(3) string interface to the native" 55 " \bsysconf\b(2), \bconfstr\b(2), \bpathconf\b(2), and \bsysinfo\b(2)" 56 " system calls. If \bgetconf\b on \b$PATH\b is not the default native" 57 " \bgetconf\b, named by \b$(getconf GETCONF)\b, then \bastgetconf\b(3)" 58 " checks only \bast\b specific extensions and the native system calls;" 59 " invalid options and/or names not supported by \bastgetconf\b(3) cause" 60 " the \bgetconf\b on \b$PATH\b to be executed.]" 61 62 "[a:all?Call the native \bgetconf\b(1) with option \b-a\b.]" 63 "[b:base?List base variable name sans call and standard prefixes.]" 64 "[c:call?Display variables with call prefix that matches \aRE\a. The call" 65 " prefixes are:]:[RE]{" 66 " [+CS?\bconfstr\b(2)]" 67 " [+PC?\bpathconf\b(2)]" 68 " [+SC?\bsysconf\b(2)]" 69 " [+SI?\bsysinfo\b(2)]" 70 " [+XX?Constant value.]" 71 "}" 72 "[d:defined?Only display defined values when no operands are specified.]" 73 "[l:lowercase?List variable names in lower case.]" 74 "[n:name?Display variables with name that match \aRE\a.]:[RE]" 75 "[p:portable?Display the named \bwritable\b variables and values in a form that" 76 " can be directly executed by \bsh\b(1) to set the values. If \aname\a" 77 " is omitted then all \bwritable\b variables are listed.]" 78 "[q:quote?\"...\" quote values.]" 79 "[r:readonly?Display the named \breadonly\b variables in \aname\a=\avalue\a form." 80 " If \aname\a is omitted then all \breadonly\b variables are listed.]" 81 "[s:standard?Display variables with standard prefix that matches \aRE\a." 82 " Use the \b--table\b option to view all standard prefixes, including" 83 " local additions. The standard prefixes available on all systems" 84 " are:]:[RE]{" 85 " [+AES]" 86 " [+AST]" 87 " [+C]" 88 " [+GNU]" 89 " [+POSIX]" 90 " [+SVID]" 91 " [+XBS5]" 92 " [+XOPEN]" 93 " [+XPG]" 94 "}" 95 "[t:table?Display the internal table that contains the name, standard," 96 " standard section, and system call symbol prefix for each variable.]" 97 "[w:writable?Display the named \bwritable\b variables in \aname\a=\avalue\a" 98 " form. If \aname\a is omitted then all \bwritable\b variables are" 99 " listed.]" 100 "[v:specification?Call the native \bgetconf\b(1) with option" 101 " \b-v\b \aname\a.]:[name]" 102 103 "\n" 104 "\n[ name [ path [ value ] ] ... ]\n" 105 "\n" 106 107 "[+ENVIRONMENT]{" 108 " [+_AST_FEATURES?Process local writable values that are different from" 109 " the default are stored in the \b_AST_FEATURES\b environment" 110 " variable. The \b_AST_FEATURES\b value is a space-separated" 111 " list of \aname\a \apath\a \avalue\a 3-tuples, where" 112 " \aname\a is the system configuration name, \apath\a is the" 113 " corresponding path, \b-\b if no path is applicable, and" 114 " \avalue\a is the system configuration value.]" 115 "}" 116 "[+SEE ALSO?\bpathchk\b(1), \bconfstr\b(2), \bpathconf\b(2)," 117 " \bsysconf\b(2), \bastgetconf\b(3)]" 118 ; 119 120 #include <cmd.h> 121 #include <proc.h> 122 #include <ls.h> 123 124 typedef struct Path_s 125 { 126 const char* path; 127 int len; 128 } Path_t; 129 130 int 131 b_getconf(int argc, char** argv, void* context) 132 { 133 register char* name; 134 register char* path; 135 register char* value; 136 register const char* s; 137 register const char* t; 138 char* pattern; 139 char* native; 140 char* cmd; 141 Path_t* e; 142 Path_t* p; 143 int flags; 144 int n; 145 int i; 146 int m; 147 int q; 148 char** oargv; 149 char buf[PATH_MAX]; 150 Path_t std[64]; 151 struct stat st0; 152 struct stat st1; 153 154 static const char empty[] = "-"; 155 static const Path_t equiv[] = { { "/bin", 4 }, { "/usr/bin", 8 } }; 156 157 cmdinit(argc, argv, context, ERROR_CATALOG, 0); 158 oargv = argv; 159 if (*(native = astconf("GETCONF", NiL, NiL)) != '/') 160 native = 0; 161 flags = 0; 162 name = 0; 163 pattern = 0; 164 for (;;) 165 { 166 switch (optget(argv, usage)) 167 { 168 case 'a': 169 if (native) 170 goto defer; 171 continue; 172 case 'b': 173 flags |= ASTCONF_base; 174 continue; 175 case 'c': 176 flags |= ASTCONF_matchcall; 177 pattern = opt_info.arg; 178 continue; 179 case 'd': 180 flags |= ASTCONF_defined; 181 continue; 182 case 'l': 183 flags |= ASTCONF_lower; 184 continue; 185 case 'n': 186 flags |= ASTCONF_matchname; 187 pattern = opt_info.arg; 188 continue; 189 case 'p': 190 flags |= ASTCONF_parse; 191 continue; 192 case 'q': 193 flags |= ASTCONF_quote; 194 continue; 195 case 'r': 196 flags |= ASTCONF_read; 197 continue; 198 case 's': 199 flags |= ASTCONF_matchstandard; 200 pattern = opt_info.arg; 201 continue; 202 case 't': 203 flags |= ASTCONF_table; 204 continue; 205 case 'v': 206 if (native) 207 goto defer; 208 continue; 209 case 'w': 210 flags |= ASTCONF_write; 211 continue; 212 case ':': 213 if (native) 214 goto defer; 215 error(2, "%s", opt_info.arg); 216 break; 217 case '?': 218 error(ERROR_usage(2), "%s", opt_info.arg); 219 break; 220 } 221 break; 222 } 223 argv += opt_info.index; 224 if (!(name = *argv)) 225 path = 0; 226 else if (streq(name, empty)) 227 { 228 name = 0; 229 if (path = *++argv) 230 { 231 argv++; 232 if (streq(path, empty)) 233 path = 0; 234 } 235 } 236 if (error_info.errors || !name && *argv) 237 error(ERROR_usage(2), "%s", optusage(NiL)); 238 if (!name) 239 astconflist(sfstdout, path, flags, pattern); 240 else 241 { 242 flags = native ? (ASTCONF_system|ASTCONF_error) : 0; 243 do 244 { 245 if (!(path = *++argv)) 246 value = 0; 247 else 248 { 249 if (streq(path, empty)) 250 { 251 path = 0; 252 flags = 0; 253 } 254 if ((value = *++argv) && (streq(value, empty))) 255 { 256 value = 0; 257 flags = 0; 258 } 259 } 260 s = astgetconf(name, path, value, flags, errorf); 261 if (error_info.errors) 262 break; 263 if (!s) 264 goto defer; 265 if (!value) 266 { 267 if (flags & ASTCONF_write) 268 { 269 sfputr(sfstdout, name, ' '); 270 sfputr(sfstdout, path ? path : empty, ' '); 271 } 272 sfputr(sfstdout, s, '\n'); 273 } 274 } while (*argv && (name = *++argv)); 275 } 276 return error_info.errors != 0; 277 278 defer: 279 280 /* 281 * defer to argv[0] if absolute and it exists 282 */ 283 284 if ((cmd = oargv[0]) && *cmd == '/' && !access(cmd, X_OK)) 285 goto found; 286 287 /* 288 * defer to the first getconf on $PATH that is also on the standard PATH 289 */ 290 291 e = std; 292 s = astconf("PATH", NiL, NiL); 293 q = !stat(equiv[0].path, &st0) && !stat(equiv[1].path, &st1) && st0.st_ino == st1.st_ino && st0.st_dev == st1.st_dev; 294 m = 0; 295 do 296 { 297 for (t = s; *s && *s != ':'; s++); 298 if ((n = s - t) && *t == '/') 299 { 300 if (q) 301 for (i = 0; i < 2; i++) 302 if (n == equiv[i].len && !strncmp(t, equiv[i].path, n)) 303 { 304 if (m & (i+1)) 305 t = 0; 306 else 307 { 308 m |= (i+1); 309 if (!(m & (!i+1))) 310 { 311 m |= (!i+1); 312 e->path = t; 313 e->len = n; 314 e++; 315 if (e >= &std[elementsof(std)]) 316 break; 317 t = equiv[!i].path; 318 n = equiv[!i].len; 319 } 320 } 321 } 322 if (t) 323 { 324 e->path = t; 325 e->len = n; 326 e++; 327 } 328 } 329 while (*s == ':') 330 s++; 331 } while (*s && e < &std[elementsof(std)]); 332 if (e < &std[elementsof(std)]) 333 { 334 e->len = strlen(e->path = "/usr/sbin"); 335 if (++e < &std[elementsof(std)]) 336 { 337 e->len = strlen(e->path = "/sbin"); 338 e++; 339 } 340 } 341 if (s = getenv("PATH")) 342 do 343 { 344 for (t = s; *s && *s != ':'; s++); 345 if ((n = s - t) && *t == '/') 346 { 347 for (p = std; p < e; p++) 348 if (p->len == n && !strncmp(t, p->path, n)) 349 { 350 sfsprintf(buf, sizeof(buf), "%-*.*s/%s", n, n, t, error_info.id); 351 if (!access(buf, X_OK)) 352 { 353 cmd = buf; 354 goto found; 355 } 356 } 357 } 358 while (*s == ':') 359 s++; 360 } while (*s); 361 362 /* 363 * defer to the first getconf on the standard PATH 364 */ 365 366 for (p = std; p < e; p++) 367 { 368 sfsprintf(buf, sizeof(buf), "%-*.*s/%s", p->len, p->len, p->path, error_info.id); 369 if (!access(buf, X_OK)) 370 { 371 cmd = buf; 372 goto found; 373 } 374 } 375 376 /* 377 * out of deferrals 378 */ 379 380 if (name) 381 error(4, "%s: unknown name -- no native getconf(1) to defer to", name); 382 else 383 error(4, "no native getconf(1) to defer to"); 384 return 2; 385 386 found: 387 388 /* 389 * don't blame us for crappy diagnostics 390 */ 391 392 oargv[0] = cmd; 393 if ((n = sh_run(context, argc, oargv)) >= EXIT_NOEXEC) 394 error(ERROR_SYSTEM|2, "%s: exec error [%d]", cmd, n); 395 return n; 396 } 397