xref: /titanic_50/usr/src/lib/libcmd/common/getconf.c (revision ff3124eff995e6cd8ebd8c6543648e0670920034)
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