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