getconf.c revision 1
1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1992-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Glenn Fowler
1N/A * AT&T Research
1N/A *
1N/A * getconf - get configuration values
1N/A */
1N/A
1N/Astatic const char usage[] =
1N/A"[-?\n@(#)$Id: getconf (AT&T Research) 2008-04-24 $\n]"
1N/AUSAGE_LICENSE
1N/A"[+NAME?getconf - get configuration values]"
1N/A"[+DESCRIPTION?\bgetconf\b displays the system configuration value for"
1N/A" \aname\a. If \aname\a is a filesystem specific variable then"
1N/A" the value is determined relative to \apath\a or the current"
1N/A" directory if \apath\a is omitted. If \avalue\a is specified then"
1N/A" \bgetconf\b attempts to change the process local value to \avalue\a."
1N/A" \b-\b may be used in place of \apath\a when it is not relevant."
1N/A" If \apath\a is \b=\b then the the \avalue\a is cached and used"
1N/A" for subsequent tests in the calling and all child processes."
1N/A" Only \bwritable\b variables may be set; \breadonly\b variables"
1N/A" cannot be changed.]"
1N/A"[+?The current value for \aname\a is written to the standard output. If"
1N/A" \aname\a is valid but undefined then \bundefined\b is written to"
1N/A" the standard output. If \aname\a is invalid or an error occurs in"
1N/A" determining its value, then a diagnostic written to the standard error"
1N/A" and \bgetconf\b exits with a non-zero exit status.]"
1N/A"[+?More than one variable may be set or queried by providing the \aname\a"
1N/A" \apath\a \avalue\a 3-tuple for each variable, specifying \b-\b for"
1N/A" \avalue\a when querying.]"
1N/A"[+?If no operands are specified then all known variables are written in"
1N/A" \aname\a=\avalue\a form to the standard output, one per line."
1N/A" Only one of \b--call\b, \b--name\b or \b--standard\b may be specified.]"
1N/A"[+?This implementation uses the \bastgetconf\b(3) string interface to the native"
1N/A" \bsysconf\b(2), \bconfstr\b(2), \bpathconf\b(2), and \bsysinfo\b(2)"
1N/A" system calls. If \bgetconf\b on \b$PATH\b is not the default native"
1N/A" \bgetconf\b, named by \b$(getconf GETCONF)\b, then \bastgetconf\b(3)"
1N/A" checks only \bast\b specific extensions and the native system calls;"
1N/A" invalid options and/or names not supported by \bastgetconf\b(3) cause"
1N/A" the \bgetconf\b on \b$PATH\b to be executed.]"
1N/A
1N/A"[a:all?Call the native \bgetconf\b(1) with option \b-a\b.]"
1N/A"[b:base?List base variable name sans call and standard prefixes.]"
1N/A"[c:call?Display variables with call prefix that matches \aRE\a. The call"
1N/A" prefixes are:]:[RE]{"
1N/A" [+CS?\bconfstr\b(2)]"
1N/A" [+PC?\bpathconf\b(2)]"
1N/A" [+SC?\bsysconf\b(2)]"
1N/A" [+SI?\bsysinfo\b(2)]"
1N/A" [+XX?Constant value.]"
1N/A"}"
1N/A"[d:defined?Only display defined values when no operands are specified.]"
1N/A"[l:lowercase?List variable names in lower case.]"
1N/A"[n:name?Display variables with name that match \aRE\a.]:[RE]"
1N/A"[p:portable?Display the named \bwritable\b variables and values in a form that"
1N/A" can be directly executed by \bsh\b(1) to set the values. If \aname\a"
1N/A" is omitted then all \bwritable\b variables are listed.]"
1N/A"[q:quote?\"...\" quote values.]"
1N/A"[r:readonly?Display the named \breadonly\b variables in \aname\a=\avalue\a form."
1N/A" If \aname\a is omitted then all \breadonly\b variables are listed.]"
1N/A"[s:standard?Display variables with standard prefix that matches \aRE\a."
1N/A" Use the \b--table\b option to view all standard prefixes, including"
1N/A" local additions. The standard prefixes available on all systems"
1N/A" are:]:[RE]{"
1N/A" [+AES]"
1N/A" [+AST]"
1N/A" [+C]"
1N/A" [+GNU]"
1N/A" [+POSIX]"
1N/A" [+SVID]"
1N/A" [+XBS5]"
1N/A" [+XOPEN]"
1N/A" [+XPG]"
1N/A"}"
1N/A"[t:table?Display the internal table that contains the name, standard,"
1N/A" standard section, and system call symbol prefix for each variable.]"
1N/A"[w:writable?Display the named \bwritable\b variables in \aname\a=\avalue\a"
1N/A" form. If \aname\a is omitted then all \bwritable\b variables are"
1N/A" listed.]"
1N/A"[v:specification?Call the native \bgetconf\b(1) with option"
1N/A" \b-v\b \aname\a.]:[name]"
1N/A
1N/A"\n"
1N/A"\n[ name [ path [ value ] ] ... ]\n"
1N/A"\n"
1N/A
1N/A"[+ENVIRONMENT]{"
1N/A" [+_AST_FEATURES?Process local writable values that are different from"
1N/A" the default are stored in the \b_AST_FEATURES\b environment"
1N/A" variable. The \b_AST_FEATURES\b value is a space-separated"
1N/A" list of \aname\a \apath\a \avalue\a 3-tuples, where"
1N/A" \aname\a is the system configuration name, \apath\a is the"
1N/A" corresponding path, \b-\b if no path is applicable, and"
1N/A" \avalue\a is the system configuration value.]"
1N/A"}"
1N/A"[+SEE ALSO?\bpathchk\b(1), \bconfstr\b(2), \bpathconf\b(2),"
1N/A" \bsysconf\b(2), \bastgetconf\b(3)]"
1N/A;
1N/A
1N/A#include <cmd.h>
1N/A#include <proc.h>
1N/A#include <ls.h>
1N/A
1N/Atypedef struct Path_s
1N/A{
1N/A const char* path;
1N/A int len;
1N/A} Path_t;
1N/A
1N/Aint
1N/Ab_getconf(int argc, char** argv, void* context)
1N/A{
1N/A register char* name;
1N/A register char* path;
1N/A register char* value;
1N/A register const char* s;
1N/A register const char* t;
1N/A char* pattern;
1N/A char* native;
1N/A char* cmd;
1N/A Path_t* e;
1N/A Path_t* p;
1N/A int flags;
1N/A int n;
1N/A int i;
1N/A int m;
1N/A int q;
1N/A char** oargv;
1N/A char buf[PATH_MAX];
1N/A Path_t std[64];
1N/A struct stat st0;
1N/A struct stat st1;
1N/A
1N/A static const char empty[] = "-";
1N/A static const Path_t equiv[] = { { "/bin", 4 }, { "/usr/bin", 8 } };
1N/A
1N/A cmdinit(argc, argv, context, ERROR_CATALOG, 0);
1N/A oargv = argv;
1N/A if (*(native = astconf("GETCONF", NiL, NiL)) != '/')
1N/A native = 0;
1N/A flags = 0;
1N/A name = 0;
1N/A pattern = 0;
1N/A for (;;)
1N/A {
1N/A switch (optget(argv, usage))
1N/A {
1N/A case 'a':
1N/A if (native)
1N/A goto defer;
1N/A continue;
1N/A case 'b':
1N/A flags |= ASTCONF_base;
1N/A continue;
1N/A case 'c':
1N/A flags |= ASTCONF_matchcall;
1N/A pattern = opt_info.arg;
1N/A continue;
1N/A case 'd':
1N/A flags |= ASTCONF_defined;
1N/A continue;
1N/A case 'l':
1N/A flags |= ASTCONF_lower;
1N/A continue;
1N/A case 'n':
1N/A flags |= ASTCONF_matchname;
1N/A pattern = opt_info.arg;
1N/A continue;
1N/A case 'p':
1N/A flags |= ASTCONF_parse;
1N/A continue;
1N/A case 'q':
1N/A flags |= ASTCONF_quote;
1N/A continue;
1N/A case 'r':
1N/A flags |= ASTCONF_read;
1N/A continue;
1N/A case 's':
1N/A flags |= ASTCONF_matchstandard;
1N/A pattern = opt_info.arg;
1N/A continue;
1N/A case 't':
1N/A flags |= ASTCONF_table;
1N/A continue;
1N/A case 'v':
1N/A if (native)
1N/A goto defer;
1N/A continue;
1N/A case 'w':
1N/A flags |= ASTCONF_write;
1N/A continue;
1N/A case ':':
1N/A if (native)
1N/A goto defer;
1N/A error(2, "%s", opt_info.arg);
1N/A break;
1N/A case '?':
1N/A error(ERROR_usage(2), "%s", opt_info.arg);
1N/A break;
1N/A }
1N/A break;
1N/A }
1N/A argv += opt_info.index;
1N/A if (!(name = *argv))
1N/A path = 0;
1N/A else if (streq(name, empty))
1N/A {
1N/A name = 0;
1N/A if (path = *++argv)
1N/A {
1N/A argv++;
1N/A if (streq(path, empty))
1N/A path = 0;
1N/A }
1N/A }
1N/A if (error_info.errors || !name && *argv)
1N/A error(ERROR_usage(2), "%s", optusage(NiL));
1N/A if (!name)
1N/A astconflist(sfstdout, path, flags, pattern);
1N/A else
1N/A {
1N/A flags = native ? (ASTCONF_system|ASTCONF_error) : 0;
1N/A do
1N/A {
1N/A if (!(path = *++argv))
1N/A value = 0;
1N/A else
1N/A {
1N/A if (streq(path, empty))
1N/A {
1N/A path = 0;
1N/A flags = 0;
1N/A }
1N/A if ((value = *++argv) && (streq(value, empty)))
1N/A {
1N/A value = 0;
1N/A flags = 0;
1N/A }
1N/A }
1N/A s = astgetconf(name, path, value, flags, errorf);
1N/A if (error_info.errors)
1N/A break;
1N/A if (!s)
1N/A goto defer;
1N/A if (!value)
1N/A {
1N/A if (flags & ASTCONF_write)
1N/A {
1N/A sfputr(sfstdout, name, ' ');
1N/A sfputr(sfstdout, path ? path : empty, ' ');
1N/A }
1N/A sfputr(sfstdout, s, '\n');
1N/A }
1N/A } while (*argv && (name = *++argv));
1N/A }
1N/A return error_info.errors != 0;
1N/A
1N/A defer:
1N/A
1N/A /*
1N/A * defer to argv[0] if absolute and it exists
1N/A */
1N/A
1N/A if ((cmd = oargv[0]) && *cmd == '/' && !access(cmd, X_OK))
1N/A goto found;
1N/A
1N/A /*
1N/A * defer to the first getconf on $PATH that is also on the standard PATH
1N/A */
1N/A
1N/A e = std;
1N/A s = astconf("PATH", NiL, NiL);
1N/A q = !stat(equiv[0].path, &st0) && !stat(equiv[1].path, &st1) && st0.st_ino == st1.st_ino && st0.st_dev == st1.st_dev;
1N/A m = 0;
1N/A do
1N/A {
1N/A for (t = s; *s && *s != ':'; s++);
1N/A if ((n = s - t) && *t == '/')
1N/A {
1N/A if (q)
1N/A for (i = 0; i < 2; i++)
1N/A if (n == equiv[i].len && !strncmp(t, equiv[i].path, n))
1N/A {
1N/A if (m & (i+1))
1N/A t = 0;
1N/A else
1N/A {
1N/A m |= (i+1);
1N/A if (!(m & (!i+1)))
1N/A {
1N/A m |= (!i+1);
1N/A e->path = t;
1N/A e->len = n;
1N/A e++;
1N/A if (e >= &std[elementsof(std)])
1N/A break;
1N/A t = equiv[!i].path;
1N/A n = equiv[!i].len;
1N/A }
1N/A }
1N/A }
1N/A if (t)
1N/A {
1N/A e->path = t;
1N/A e->len = n;
1N/A e++;
1N/A }
1N/A }
1N/A while (*s == ':')
1N/A s++;
1N/A } while (*s && e < &std[elementsof(std)]);
1N/A if (e < &std[elementsof(std)])
1N/A {
1N/A e->len = strlen(e->path = "/usr/sbin");
1N/A if (++e < &std[elementsof(std)])
1N/A {
1N/A e->len = strlen(e->path = "/sbin");
1N/A e++;
1N/A }
1N/A }
1N/A if (s = getenv("PATH"))
1N/A do
1N/A {
1N/A for (t = s; *s && *s != ':'; s++);
1N/A if ((n = s - t) && *t == '/')
1N/A {
1N/A for (p = std; p < e; p++)
1N/A if (p->len == n && !strncmp(t, p->path, n))
1N/A {
1N/A sfsprintf(buf, sizeof(buf), "%-*.*s/%s", n, n, t, error_info.id);
1N/A if (!access(buf, X_OK))
1N/A {
1N/A cmd = buf;
1N/A goto found;
1N/A }
1N/A }
1N/A }
1N/A while (*s == ':')
1N/A s++;
1N/A } while (*s);
1N/A
1N/A /*
1N/A * defer to the first getconf on the standard PATH
1N/A */
1N/A
1N/A for (p = std; p < e; p++)
1N/A {
1N/A sfsprintf(buf, sizeof(buf), "%-*.*s/%s", p->len, p->len, p->path, error_info.id);
1N/A if (!access(buf, X_OK))
1N/A {
1N/A cmd = buf;
1N/A goto found;
1N/A }
1N/A }
1N/A
1N/A /*
1N/A * out of deferrals
1N/A */
1N/A
1N/A if (name)
1N/A error(4, "%s: unknown name -- no native getconf(1) to defer to", name);
1N/A else
1N/A error(4, "no native getconf(1) to defer to");
1N/A return 2;
1N/A
1N/A found:
1N/A
1N/A /*
1N/A * don't blame us for crappy diagnostics
1N/A */
1N/A
1N/A oargv[0] = cmd;
1N/A if ((n = sh_run(context, argc, oargv)) >= EXIT_NOEXEC)
1N/A error(ERROR_SYSTEM|2, "%s: exec error [%d]", cmd, n);
1N/A return n;
1N/A}