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