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