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