BSD 4_4 release
[unix-history] / usr / src / usr.sbin / amd / amd / opts.c
/*-
* 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;
}