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