diff options
Diffstat (limited to 'usr.sbin/amd/amd/opts.c')
-rw-r--r-- | usr.sbin/amd/amd/opts.c | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/usr.sbin/amd/amd/opts.c b/usr.sbin/amd/amd/opts.c new file mode 100644 index 0000000..54c9675 --- /dev/null +++ b/usr.sbin/amd/amd/opts.c @@ -0,0 +1,835 @@ +/*- + * Copyright (c) 1989 Jan-Simon Pendry + * Copyright (c) 1989 Imperial College of Science, Technology & Medicine + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: opts.c,v 5.2.2.3 1992/05/31 16:34:13 jsp Exp $ + */ + +#ifndef lint +static char sccsid[] = "@(#)opts.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "am.h" + +extern char *getenv P((const char *)); + +/* + * static copy of the options with + * which to play + */ +static struct am_opts fs_static; + +static char *opt_host = hostname; +static char *opt_hostd = hostd; +static char nullstr[] = ""; +static char *opt_key = nullstr; +static char *opt_map = nullstr; +static char *opt_path = nullstr; + +static char *vars[8]; + +/* + * Length of longest option name + */ +#define NLEN 16 /* conservative */ +#define S(x) (x) , (sizeof(x)-1) +static struct opt { + char *name; /* Name of the option */ + int nlen; /* Length of option name */ + char **optp; /* Pointer to option value string */ + char **sel_p; /* Pointer to selector value string */ +} opt_fields[] = { + /* Options in something corresponding to frequency of use */ + { S("opts"), &fs_static.opt_opts, 0 }, + { S("host"), 0, &opt_host }, + { S("hostd"), 0, &opt_hostd }, + { S("type"), &fs_static.opt_type, 0 }, + { S("rhost"), &fs_static.opt_rhost, 0 }, + { S("rfs"), &fs_static.opt_rfs, 0 }, + { S("fs"), &fs_static.opt_fs, 0 }, + { S("key"), 0, &opt_key }, + { S("map"), 0, &opt_map }, + { S("sublink"), &fs_static.opt_sublink, 0 }, + { S("arch"), 0, &arch }, + { S("dev"), &fs_static.opt_dev, 0 }, + { S("pref"), &fs_static.opt_pref, 0 }, + { S("path"), 0, &opt_path }, + { S("autodir"), 0, &auto_dir }, + { S("delay"), &fs_static.opt_delay, 0 }, + { S("domain"), 0, &hostdomain }, + { S("karch"), 0, &karch }, + { S("cluster"), 0, &cluster }, + { S("wire"), 0, &wire }, + { S("byte"), 0, &endian }, + { S("os"), 0, &op_sys }, + { S("remopts"), &fs_static.opt_remopts, 0 }, + { S("mount"), &fs_static.opt_mount, 0 }, + { S("unmount"), &fs_static.opt_unmount, 0 }, + { S("cache"), &fs_static.opt_cache, 0 }, + { S("user"), &fs_static.opt_user, 0 }, + { S("group"), &fs_static.opt_group, 0 }, + { S("var0"), &vars[0], 0 }, + { S("var1"), &vars[1], 0 }, + { S("var2"), &vars[2], 0 }, + { S("var3"), &vars[3], 0 }, + { S("var4"), &vars[4], 0 }, + { S("var5"), &vars[5], 0 }, + { S("var6"), &vars[6], 0 }, + { S("var7"), &vars[7], 0 }, + { 0, 0, 0, 0 }, +}; + +typedef struct opt_apply opt_apply; +struct opt_apply { + char **opt; + char *val; +}; + +/* + * Specially expand the remote host name first + */ +static opt_apply rhost_expansion[] = { + { &fs_static.opt_rhost, "${host}" }, + { 0, 0 }, +}; +/* + * List of options which need to be expanded + * Note that this the order here _may_ be important. + */ +static opt_apply expansions[] = { +/* { &fs_static.opt_dir, 0 }, */ + { &fs_static.opt_sublink, 0 }, + { &fs_static.opt_rfs, "${path}" }, + { &fs_static.opt_fs, "${autodir}/${rhost}${rfs}" }, + { &fs_static.opt_opts, "rw" }, + { &fs_static.opt_remopts, "${opts}" }, + { &fs_static.opt_mount, 0 }, + { &fs_static.opt_unmount, 0 }, + { 0, 0 }, +}; + +/* + * List of options which need to be free'ed before re-use + */ +static opt_apply to_free[] = { + { &fs_static.fs_glob, 0 }, + { &fs_static.fs_local, 0 }, + { &fs_static.fs_mtab, 0 }, +/* { &fs_static.opt_dir, 0 }, */ + { &fs_static.opt_sublink, 0 }, + { &fs_static.opt_rfs, 0 }, + { &fs_static.opt_fs, 0 }, + { &fs_static.opt_rhost, 0 }, + { &fs_static.opt_opts, 0 }, + { &fs_static.opt_remopts, 0 }, + { &fs_static.opt_mount, 0 }, + { &fs_static.opt_unmount, 0 }, + { &vars[0], 0 }, + { &vars[1], 0 }, + { &vars[2], 0 }, + { &vars[3], 0 }, + { &vars[4], 0 }, + { &vars[5], 0 }, + { &vars[6], 0 }, + { &vars[7], 0 }, + { 0, 0 }, +}; + +/* + * Skip to next option in the string + */ +static char *opt P((char**)); +static char *opt(p) +char **p; +{ + char *cp = *p; + char *dp = cp; + char *s = cp; + +top: + while (*cp && *cp != ';') { + if (*cp == '\"') { + /* + * Skip past string + */ + cp++; + while (*cp && *cp != '\"') + *dp++ = *cp++; + if (*cp) + cp++; + } else { + *dp++ = *cp++; + } + } + + /* + * Skip past any remaining ';'s + */ + while (*cp == ';') + cp++; + + /* + * If we have a zero length string + * and there are more fields, then + * parse the next one. This allows + * sequences of empty fields. + */ + if (*cp && dp == s) + goto top; + + *dp = '\0'; + + *p = cp; + return s; +} + +static int eval_opts P((char*, char*)); +static int eval_opts(opts, mapkey) +char *opts; +char *mapkey; +{ + /* + * Fill in the global structure fs_static by + * cracking the string opts. opts may be + * scribbled on at will. + */ + char *o = opts; + char *f; + + /* + * For each user-specified option + */ + while (*(f = opt(&o))) { + struct opt *op; + enum vs_opt { OldSyn, SelEQ, SelNE, VarAss } vs_opt; + char *eq = strchr(f, '='); + char *opt; + if (!eq || eq[1] == '\0' || eq == f) { + /* + * No value, just continue + */ + plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f); + continue; + } + + /* + * Check what type of operation is happening + * !=, =! is SelNE + * == is SelEQ + * := is VarAss + * = is OldSyn (either SelEQ or VarAss) + */ + if (eq[-1] == '!') { /* != */ + vs_opt = SelNE; + eq[-1] = '\0'; + opt = eq + 1; + } else if (eq[-1] == ':') { /* := */ + vs_opt = VarAss; + eq[-1] = '\0'; + opt = eq + 1; + } else if (eq[1] == '=') { /* == */ + vs_opt = SelEQ; + eq[0] = '\0'; + opt = eq + 2; + } else if (eq[1] == '!') { /* =! */ + vs_opt = SelNE; + eq[0] = '\0'; + opt = eq + 2; + } else { /* = */ + vs_opt = OldSyn; + eq[0] = '\0'; + opt = eq + 1; + } + + /* + * For each recognised option + */ + for (op = opt_fields; op->name; op++) { + /* + * Check whether they match + */ + if (FSTREQ(op->name, f)) { + switch (vs_opt) { +#if AMD_COMPAT <= 5000108 + case OldSyn: + plog(XLOG_WARNING, "key %s: Old syntax selector found: %s=%s", mapkey, f, opt); + if (!op->sel_p) { + *op->optp = opt; + break; + } + /* fall through ... */ +#endif /* 5000108 */ + case SelEQ: + case SelNE: + if (op->sel_p && (STREQ(*op->sel_p, opt) == (vs_opt == SelNE))) { + plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s", + mapkey, + op->name, + *op->sel_p, + vs_opt == SelNE ? "not " : "", + opt); + return 0; + } + break; + + case VarAss: + if (op->sel_p) { + plog(XLOG_USER, "key %s: Can't assign to a selector (%s)", mapkey, op->name); + return 0; + } + *op->optp = opt; + break; + } + break; + } + } + + if (!op->name) + plog(XLOG_USER, "key %s: Unrecognised key/option \"%s\"", mapkey, f); + } + + return 1; +} + +/* + * Free an option + */ +static void free_op P((opt_apply*, int)); +/*ARGSUSED*/ +static void free_op(p, b) +opt_apply *p; +int b; +{ + if (*p->opt) { + free(*p->opt); + *p->opt = 0; + } +} + +/* + * Normalize slashes in the string. + */ +void normalize_slash P((char *p)); +void normalize_slash(p) +char *p; +{ + char *f = strchr(p, '/'); + char *f0 = f; + if (f) { + char *t = f; + do { + /* assert(*f == '/'); */ + if (f == f0 && f[0] == '/' && f[1] == '/') { + /* copy double slash iff first */ + *t++ = *f++; + *t++ = *f++; + } else { + /* copy a single / across */ + *t++ = *f++; + } + + /* assert(f[-1] == '/'); */ + /* skip past more /'s */ + while (*f == '/') + f++; + + /* assert(*f != '/'); */ + /* keep copying up to next / */ + while (*f && *f != '/') { + *t++ = *f++; + } + + /* assert(*f == 0 || *f == '/'); */ + + } while (*f); + *t = 0; /* derived from fix by Steven Glassman */ + } +} + +/* + * Macro-expand an option. Note that this does not + * handle recursive expansions. They will go badly wrong. + * If sel is true then old expand selectors, otherwise + * don't expand selectors. + */ +static void expand_op P((opt_apply*, int)); +static void expand_op(p, sel_p) +opt_apply *p; +int sel_p; +{ +/* + * The BUFSPACE macros checks that there is enough space + * left in the expansion buffer. If there isn't then we + * give up completely. This is done to avoid crashing the + * automounter itself (which would be a bad thing to do). + */ +#define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN) +static char expand_error[] = "No space to expand \"%s\""; + + char expbuf[MAXPATHLEN+1]; + char nbuf[NLEN+1]; + char *ep = expbuf; + char *cp = *p->opt; + char *dp; +#ifdef DEBUG + char *cp_orig = *p->opt; +#endif /* DEBUG */ + struct opt *op; + + while (dp = strchr(cp, '$')) { + char ch; + /* + * First copy up to the $ + */ + { int len = dp - cp; + if (BUFSPACE(ep, len)) { + strncpy(ep, cp, len); + ep += len; + } else { + plog(XLOG_ERROR, expand_error, *p->opt); + goto out; + } + } + cp = dp + 1; + ch = *cp++; + if (ch == '$') { + if (BUFSPACE(ep, 1)) { + *ep++ = '$'; + } else { + plog(XLOG_ERROR, expand_error, *p->opt); + goto out; + } + } else if (ch == '{') { + /* Expansion... */ + enum { E_All, E_Dir, E_File, E_Domain, E_Host } todo; + /* + * Find closing brace + */ + char *br_p = strchr(cp, '}'); + int len; + /* + * Check we found it + */ + if (!br_p) { + /* + * Just give up + */ + plog(XLOG_USER, "No closing '}' in \"%s\"", *p->opt); + goto out; + } + len = br_p - cp; + /* + * Figure out which part of the variable to grab. + */ + if (*cp == '/') { + /* + * Just take the last component + */ + todo = E_File; + cp++; + --len; + } else if (br_p[-1] == '/') { + /* + * Take all but the last component + */ + todo = E_Dir; + --len; + } else if (*cp == '.') { + /* + * Take domain name + */ + todo = E_Domain; + cp++; + --len; + } else if (br_p[-1] == '.') { + /* + * Take host name + */ + todo = E_Host; + --len; + } else { + /* + * Take the whole lot + */ + todo = E_All; + } + /* + * Truncate if too long. Since it won't + * match anyway it doesn't matter that + * it has been cut short. + */ + if (len > NLEN) + len = NLEN; + /* + * Put the string into another buffer so + * we can do comparisons. + */ + strncpy(nbuf, cp, len); + nbuf[len] = '\0'; + /* + * Advance cp + */ + cp = br_p + 1; + /* + * Search the option array + */ + for (op = opt_fields; op->name; op++) { + /* + * Check for match + */ + if (len == op->nlen && STREQ(op->name, nbuf)) { + char xbuf[NLEN+3]; + char *val; + /* + * Found expansion. Copy + * the correct value field. + */ + if (!(!op->sel_p == !sel_p)) { + /* + * Copy the string across unexpanded + */ + sprintf(xbuf, "${%s%s%s}", + todo == E_File ? "/" : + todo == E_Domain ? "." : "", + nbuf, + todo == E_Dir ? "/" : + todo == E_Host ? "." : ""); + val = xbuf; + /* + * Make sure expansion doesn't + * munge the value! + */ + todo = E_All; + } else if (op->sel_p) { + val = *op->sel_p; + } else { + val = *op->optp; + } + if (val) { + /* + * Do expansion: + * ${/var} means take just the last part + * ${var/} means take all but the last part + * ${.var} means take all but first part + * ${var.} means take just the first part + * ${var} means take the whole lot + */ + int vlen = strlen(val); + char *vptr = val; + switch (todo) { + case E_Dir: + vptr = strrchr(val, '/'); + if (vptr) + vlen = vptr - val; + vptr = val; + break; + case E_File: + vptr = strrchr(val, '/'); + if (vptr) { + vptr++; + vlen = strlen(vptr); + } else + vptr = val; + break; + case E_Domain: + vptr = strchr(val, '.'); + if (vptr) { + vptr++; + vlen = strlen(vptr); + } else { + vptr = ""; + vlen = 0; + } + break; + case E_Host: + vptr = strchr(val, '.'); + if (vptr) + vlen = vptr - val; + vptr = val; + break; + case E_All: + break; + } +#ifdef DEBUG + /*dlog("Expanding \"%s\" to \"%s\"", nbuf, val);*/ +#endif /* DEBUG */ + if (BUFSPACE(ep, vlen)) { + strcpy(ep, vptr); + ep += vlen; + } else { + plog(XLOG_ERROR, expand_error, *p->opt); + goto out; + } + } + /* + * Done with this variable + */ + break; + } + } + /* + * Check that the search was succesful + */ + if (!op->name) { + /* + * If it wasn't then scan the + * environment for that name + * and use any value found + */ + char *env = getenv(nbuf); + if (env) { + int vlen = strlen(env); + + if (BUFSPACE(ep, vlen)) { + strcpy(ep, env); + ep += vlen; + } else { + plog(XLOG_ERROR, expand_error, *p->opt); + goto out; + } +#ifdef DEBUG + Debug(D_STR) + plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env); +#endif /* DEBUG */ + } else { + plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf); + } + } + } else { + /* + * Error, error + */ + plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt); + } + } + +out: + /* + * Handle common case - no expansion + */ + if (cp == *p->opt) { + *p->opt = strdup(cp); + } else { + /* + * Finish off the expansion + */ + if (BUFSPACE(ep, strlen(cp))) { + strcpy(ep, cp); + /*ep += strlen(ep);*/ + } else { + plog(XLOG_ERROR, expand_error, *p->opt); + } + + /* + * Save the exansion + */ + *p->opt = strdup(expbuf); + } + + normalize_slash(*p->opt); + +#ifdef DEBUG + Debug(D_STR) { + plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig); + plog(XLOG_DEBUG, "... is \"%s\"", *p->opt); + } +#endif /* DEBUG */ +} + +/* + * Wrapper for expand_op + */ +static void expand_opts P((opt_apply*, int)); +static void expand_opts(p, sel_p) +opt_apply *p; +int sel_p; +{ + if (*p->opt) { + expand_op(p, sel_p); + } else if (p->val) { + /* + * Do double expansion, remembering + * to free the string from the first + * expansion... + */ + char *s = *p->opt = expand_key(p->val); + expand_op(p, sel_p); + free(s); + } +} + +/* + * Apply a function to a list of options + */ +static void apply_opts(op, ppp, b) +void (*op)(); +opt_apply ppp[]; +int b; +{ + opt_apply *pp; + for (pp = ppp; pp->opt; pp++) + (*op)(pp, b); +} + +/* + * Free the option table + */ +void free_opts(fo) +am_opts *fo; +{ + /* + * Copy in the structure we are playing with + */ + fs_static = *fo; + + /* + * Free previously allocated memory + */ + apply_opts(free_op, to_free, FALSE); +} + +/* + * Expand lookup key + */ +char *expand_key(key) +char *key; +{ + opt_apply oa; + + oa.opt = &key; oa.val = 0; + expand_opts(&oa, TRUE); + + return key; +} + +/* + * Remove trailing /'s from a string + * unless the string is a single / (Steven Glassman) + */ +void deslashify P((char *s)); +void deslashify(s) +char *s; +{ + if (s && *s) { + char *sl = s + strlen(s); + while (*--sl == '/' && sl > s) + *sl = '\0'; + } +} + +int eval_fs_opts(fo, opts, g_opts, path, key, map) +am_opts *fo; +char *opts, *g_opts, *path, *key, *map; +{ + int ok = TRUE; + + free_opts(fo); + + /* + * Clear out the option table + */ + bzero((voidp) &fs_static, sizeof(fs_static)); + bzero((voidp) vars, sizeof(vars)); + bzero((voidp) fo, sizeof(*fo)); + + /* + * Set key, map & path before expansion + */ + opt_key = key; + opt_map = map; + opt_path = path; + + /* + * Expand global options + */ + fs_static.fs_glob = expand_key(g_opts); + + /* + * Expand local options + */ + fs_static.fs_local = expand_key(opts); + + /* + * Expand default (global) options + */ + if (!eval_opts(fs_static.fs_glob, key)) + ok = FALSE; + + /* + * Expand local options + */ + if (ok && !eval_opts(fs_static.fs_local, key)) + ok = FALSE; + + /* + * Normalise remote host name. + * 1. Expand variables + * 2. Normalize relative to host tables + * 3. Strip local domains from the remote host + * name before using it in other expansions. + * This makes mount point names and other things + * much shorter, while allowing cross domain + * sharing of mount maps. + */ + apply_opts(expand_opts, rhost_expansion, FALSE); + if (ok && fs_static.opt_rhost && *fs_static.opt_rhost) + host_normalize(&fs_static.opt_rhost); + + /* + * Macro expand the options. + * Do this regardless of whether we are accepting + * this mount - otherwise nasty things happen + * with memory allocation. + */ + apply_opts(expand_opts, expansions, FALSE); + + /* + * Strip trailing slashes from local pathname... + */ + deslashify(fs_static.opt_fs); + + /* + * ok... copy the data back out. + */ + *fo = fs_static; + + /* + * Clear defined options + */ + opt_key = opt_map = opt_path = nullstr; + + return ok; +} |