1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1992-2012 AT&T Intellectual Property * 5* and is licensed under the * 6* Eclipse Public License, Version 1.0 * 7* by AT&T Intellectual Property * 8* * 9* A copy of the License is available at * 10* http://www.eclipse.org/org/documents/epl-v10.html * 11* (with md5 checksum b35adb5213ca9657e911e9befb180842) * 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 29static const char usage[] = 30"[-?\n@(#)$Id: getconf (AT&T Research) 2012-06-25 $\n]" 31USAGE_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 "{" 109 "[+_AST_FEATURES?Process local writable values that are " 110 "different from the default are stored in the \b_AST_FEATURES\b " 111 "environment variable. The \b_AST_FEATURES\b value is a " 112 "space-separated list of \aname\a \apath\a \avalue\a 3-tuples, " 113 "where \aname\a is the system configuration name, \apath\a is " 114 "the corresponding path, \b-\b if no path is applicable, and " 115 "\avalue\a is the system configuration value. \b_AST_FEATURES\b " 116 "is an implementation detail of process inheritance; it may " 117 "change or vanish in the future; don't rely on it.]" 118 "}" 119"[+SEE ALSO?\bpathchk\b(1), \bconfstr\b(2), \bpathconf\b(2)," 120" \bsysconf\b(2), \bastgetconf\b(3)]" 121; 122 123#include <cmd.h> 124#include <proc.h> 125#include <ls.h> 126 127typedef struct Path_s 128{ 129 const char* path; 130 int len; 131} Path_t; 132 133int 134b_getconf(int argc, char** argv, Shbltin_t* context) 135{ 136 register char* name; 137 register char* path; 138 register char* value; 139 register const char* s; 140 register const char* t; 141 char* pattern; 142 char* native; 143 char* cmd; 144 Path_t* e; 145 Path_t* p; 146 int flags; 147 int n; 148 int i; 149 int m; 150 int q; 151 char** oargv; 152 char buf[PATH_MAX]; 153 Path_t std[64]; 154 struct stat st0; 155 struct stat st1; 156 157 static const char empty[] = "-"; 158 static const Path_t equiv[] = { { "/bin", 4 }, { "/usr/bin", 8 } }; 159 160 cmdinit(argc, argv, context, ERROR_CATALOG, 0); 161 oargv = argv; 162 if (*(native = astconf("GETCONF", NiL, NiL)) != '/') 163 native = 0; 164 flags = 0; 165 name = 0; 166 pattern = 0; 167 for (;;) 168 { 169 switch (optget(argv, usage)) 170 { 171 case 'a': 172 if (native) 173 goto defer; 174 continue; 175 case 'b': 176 flags |= ASTCONF_base; 177 continue; 178 case 'c': 179 flags |= ASTCONF_matchcall; 180 pattern = opt_info.arg; 181 continue; 182 case 'd': 183 flags |= ASTCONF_defined; 184 continue; 185 case 'l': 186 flags |= ASTCONF_lower; 187 continue; 188 case 'n': 189 flags |= ASTCONF_matchname; 190 pattern = opt_info.arg; 191 continue; 192 case 'p': 193 flags |= ASTCONF_parse; 194 continue; 195 case 'q': 196 flags |= ASTCONF_quote; 197 continue; 198 case 'r': 199 flags |= ASTCONF_read; 200 continue; 201 case 's': 202 flags |= ASTCONF_matchstandard; 203 pattern = opt_info.arg; 204 continue; 205 case 't': 206 flags |= ASTCONF_table; 207 continue; 208 case 'v': 209 if (native) 210 goto defer; 211 continue; 212 case 'w': 213 flags |= ASTCONF_write; 214 continue; 215 case ':': 216 if (native) 217 goto defer; 218 error(2, "%s", opt_info.arg); 219 break; 220 case '?': 221 error(ERROR_usage(2), "%s", opt_info.arg); 222 break; 223 } 224 break; 225 } 226 argv += opt_info.index; 227 if (!(name = *argv)) 228 path = 0; 229 else if (streq(name, empty)) 230 { 231 name = 0; 232 if (path = *++argv) 233 { 234 argv++; 235 if (streq(path, empty)) 236 path = 0; 237 } 238 } 239 if (error_info.errors || !name && *argv) 240 error(ERROR_usage(2), "%s", optusage(NiL)); 241 if (!name) 242 astconflist(sfstdout, path, flags, pattern); 243 else 244 { 245 if (native) 246 flags |= (ASTCONF_system|ASTCONF_error); 247 do 248 { 249 if (!(path = *++argv)) 250 value = 0; 251 else 252 { 253 if (streq(path, empty)) 254 { 255 path = 0; 256 flags = 0; 257 } 258 if ((value = *++argv) && (streq(value, empty))) 259 { 260 value = 0; 261 flags = 0; 262 } 263 } 264 s = astgetconf(name, path, value, flags, errorf); 265 if (error_info.errors) 266 break; 267 if (!s) 268 { 269 if (native) 270 goto defer; 271 error(2, "%s: unknown name", name); 272 break; 273 } 274 if (!value) 275 { 276 if (flags & ASTCONF_write) 277 { 278 sfputr(sfstdout, name, ' '); 279 sfputr(sfstdout, path ? path : empty, ' '); 280 } 281 sfputr(sfstdout, s, '\n'); 282 } 283 } while (*argv && (name = *++argv)); 284 } 285 return error_info.errors != 0; 286 287 defer: 288 289 /* 290 * defer to argv[0] if absolute and it exists 291 */ 292 293 if ((cmd = oargv[0]) && *cmd == '/' && !access(cmd, X_OK)) 294 goto found; 295 296 /* 297 * defer to the first getconf on $PATH that is also on the standard PATH 298 */ 299 300 e = std; 301 s = astconf("PATH", NiL, NiL); 302 q = !stat(equiv[0].path, &st0) && !stat(equiv[1].path, &st1) && st0.st_ino == st1.st_ino && st0.st_dev == st1.st_dev; 303 m = 0; 304 do 305 { 306 for (t = s; *s && *s != ':'; s++); 307 if ((n = s - t) && *t == '/') 308 { 309 if (q) 310 for (i = 0; i < 2; i++) 311 if (n == equiv[i].len && !strncmp(t, equiv[i].path, n)) 312 { 313 if (m & (i+1)) 314 t = 0; 315 else 316 { 317 m |= (i+1); 318 if (!(m & (!i+1))) 319 { 320 m |= (!i+1); 321 e->path = t; 322 e->len = n; 323 e++; 324 if (e >= &std[elementsof(std)]) 325 break; 326 t = equiv[!i].path; 327 n = equiv[!i].len; 328 } 329 } 330 } 331 if (t) 332 { 333 e->path = t; 334 e->len = n; 335 e++; 336 } 337 } 338 while (*s == ':') 339 s++; 340 } while (*s && e < &std[elementsof(std)]); 341 if (e < &std[elementsof(std)]) 342 { 343 e->len = strlen(e->path = "/usr/sbin"); 344 if (++e < &std[elementsof(std)]) 345 { 346 e->len = strlen(e->path = "/sbin"); 347 e++; 348 } 349 } 350 if (s = getenv("PATH")) 351 do 352 { 353 for (t = s; *s && *s != ':'; s++); 354 if ((n = s - t) && *t == '/') 355 { 356 for (p = std; p < e; p++) 357 if (p->len == n && !strncmp(t, p->path, n)) 358 { 359 sfsprintf(buf, sizeof(buf), "%-*.*s/%s", n, n, t, error_info.id); 360 if (!access(buf, X_OK)) 361 { 362 cmd = buf; 363 goto found; 364 } 365 } 366 } 367 while (*s == ':') 368 s++; 369 } while (*s); 370 371 /* 372 * defer to the first getconf on the standard PATH 373 */ 374 375 for (p = std; p < e; p++) 376 { 377 sfsprintf(buf, sizeof(buf), "%-*.*s/%s", p->len, p->len, p->path, error_info.id); 378 if (!access(buf, X_OK)) 379 { 380 cmd = buf; 381 goto found; 382 } 383 } 384 385 /* 386 * out of deferrals 387 */ 388 389 if (name) 390 error(4, "%s: unknown name -- no native getconf(1) to defer to", name); 391 else 392 error(4, "no native getconf(1) to defer to"); 393 return 2; 394 395 found: 396 397 /* 398 * don't blame us for crappy diagnostics 399 */ 400 401 oargv[0] = cmd; 402 if ((n = sh_run(context, argc, oargv)) >= EXIT_NOEXEC) 403 error(ERROR_SYSTEM|2, "%s: exec error [%d]", cmd, n); 404 return n; 405} 406