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 29 static const char usage[] = 30 "[-?\n@(#)$Id: getconf (AT&T Research) 2012-06-25 $\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 "{" 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 127 typedef struct Path_s 128 { 129 const char* path; 130 int len; 131 } Path_t; 132 133 int 134 b_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