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
b_getconf(int argc,char ** argv,Shbltin_t * context)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