138494Sobrien/* 2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok 338494Sobrien * Copyright (c) 1989 Jan-Simon Pendry 438494Sobrien * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 538494Sobrien * Copyright (c) 1989 The Regents of the University of California. 638494Sobrien * All rights reserved. 738494Sobrien * 838494Sobrien * This code is derived from software contributed to Berkeley by 938494Sobrien * Jan-Simon Pendry at Imperial College, London. 1038494Sobrien * 1138494Sobrien * Redistribution and use in source and binary forms, with or without 1238494Sobrien * modification, are permitted provided that the following conditions 1338494Sobrien * are met: 1438494Sobrien * 1. Redistributions of source code must retain the above copyright 1538494Sobrien * notice, this list of conditions and the following disclaimer. 1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1738494Sobrien * notice, this list of conditions and the following disclaimer in the 1838494Sobrien * documentation and/or other materials provided with the distribution. 1938494Sobrien * 3. All advertising materials mentioning features or use of this software 2042629Sobrien * must display the following acknowledgment: 2138494Sobrien * This product includes software developed by the University of 2238494Sobrien * California, Berkeley and its contributors. 2338494Sobrien * 4. Neither the name of the University nor the names of its contributors 2438494Sobrien * may be used to endorse or promote products derived from this software 2538494Sobrien * without specific prior written permission. 2638494Sobrien * 2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3038494Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3738494Sobrien * SUCH DAMAGE. 3838494Sobrien * 3938494Sobrien * 40174294Sobrien * File: am-utils/amd/opts.c 4138494Sobrien * 4238494Sobrien */ 4338494Sobrien 4438494Sobrien#ifdef HAVE_CONFIG_H 4538494Sobrien# include <config.h> 4638494Sobrien#endif /* HAVE_CONFIG_H */ 4738494Sobrien#include <am_defs.h> 4838494Sobrien#include <amd.h> 4938494Sobrien 5038494Sobrien/* 5138494Sobrien * MACROS: 5238494Sobrien */ 5338494Sobrien#define NLEN 16 /* Length of longest option name (conservative) */ 5438494Sobrien#define S(x) (x) , (sizeof(x)-1) 5538494Sobrien/* 5638494Sobrien * The BUFSPACE macros checks that there is enough space 5738494Sobrien * left in the expansion buffer. If there isn't then we 5838494Sobrien * give up completely. This is done to avoid crashing the 5938494Sobrien * automounter itself (which would be a bad thing to do). 6038494Sobrien */ 6138494Sobrien#define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN) 6238494Sobrien 6338494Sobrien/* 6438494Sobrien * TYPEDEFS: 6538494Sobrien */ 6638494Sobrientypedef int (*IntFuncPtr) (char *); 6738494Sobrientypedef struct opt_apply opt_apply; 6838494Sobrienenum vs_opt { SelEQ, SelNE, VarAss }; 6938494Sobrien 7038494Sobrien/* 7138494Sobrien * STRUCTURES 7238494Sobrien */ 7338494Sobrienstruct opt { 7438494Sobrien char *name; /* Name of the option */ 7538494Sobrien int nlen; /* Length of option name */ 7638494Sobrien char **optp; /* Pointer to option value string */ 7738494Sobrien char **sel_p; /* Pointer to selector value string */ 7838494Sobrien int (*fxn_p)(char *); /* Pointer to boolean function */ 7938494Sobrien int case_insensitive; /* How to do selector comparisons */ 8038494Sobrien}; 8138494Sobrien 8238494Sobrienstruct opt_apply { 8338494Sobrien char **opt; 8438494Sobrien char *val; 8538494Sobrien}; 8638494Sobrien 8738494Sobrienstruct functable { 8838494Sobrien char *name; 8938494Sobrien IntFuncPtr func; 9038494Sobrien}; 9138494Sobrien 9238494Sobrien/* 9342629Sobrien * FORWARD DEFINITION: 9438494Sobrien */ 9538494Sobrienstatic int f_in_network(char *); 96174294Sobrienstatic int f_xhost(char *); 9738494Sobrienstatic int f_netgrp(char *); 9851292Sobrienstatic int f_netgrpd(char *); 9938494Sobrienstatic int f_exists(char *); 10038494Sobrienstatic int f_false(char *); 10138494Sobrienstatic int f_true(char *); 102174294Sobrienstatic inline char *expand_options(char *key); 10338494Sobrien 10438494Sobrien/* 10538494Sobrien * STATICS: 10638494Sobrien */ 10738494Sobrienstatic char NullStr[] = "<NULL>"; 10838494Sobrienstatic char nullstr[] = ""; 10938494Sobrienstatic char *opt_dkey = NullStr; 11042629Sobrienstatic char *opt_host = nullstr; /* XXX: was the global hostname */ 11138494Sobrienstatic char *opt_hostd = hostd; 11238494Sobrienstatic char *opt_key = nullstr; 11338494Sobrienstatic char *opt_keyd = nullstr; 11438494Sobrienstatic char *opt_map = nullstr; 11538494Sobrienstatic char *opt_path = nullstr; 116174294Sobrienchar uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR]; 11782794Sobrienchar *opt_uid = uid_str; 11882794Sobrienchar *opt_gid = gid_str; 11938494Sobrienstatic char *vars[8]; 12082794Sobrienstatic char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */ 12138494Sobrien 12282794Sobrien/* 12382794Sobrien * GLOBALS 12482794Sobrien */ 125174294Sobrienstatic struct am_opts fs_static; /* copy of the options to play with */ 12638494Sobrien 12782794Sobrien 12838494Sobrien/* 129174294Sobrien * Options in some order corresponding to frequency of use so that 13038494Sobrien * first-match algorithm is sped up. 13138494Sobrien */ 13238494Sobrienstatic struct opt opt_fields[] = { 13338494Sobrien /* Name and length. 13482794Sobrien Option str. Selector str. boolean fxn. case sensitive */ 13538494Sobrien { S("opts"), 13638494Sobrien &fs_static.opt_opts, 0, 0, FALSE }, 13738494Sobrien { S("host"), 13838494Sobrien 0, &opt_host, 0, TRUE }, 13938494Sobrien { S("hostd"), 14038494Sobrien 0, &opt_hostd, 0, TRUE }, 14138494Sobrien { S("type"), 14238494Sobrien &fs_static.opt_type, 0, 0, FALSE }, 14338494Sobrien { S("rhost"), 14438494Sobrien &fs_static.opt_rhost, 0, 0, TRUE }, 14538494Sobrien { S("rfs"), 14638494Sobrien &fs_static.opt_rfs, 0, 0, FALSE }, 14738494Sobrien { S("fs"), 14838494Sobrien &fs_static.opt_fs, 0, 0, FALSE }, 14938494Sobrien { S("key"), 15038494Sobrien 0, &opt_key, 0, FALSE }, 15138494Sobrien { S("map"), 15238494Sobrien 0, &opt_map, 0, FALSE }, 15338494Sobrien { S("sublink"), 15438494Sobrien &fs_static.opt_sublink, 0, 0, FALSE }, 15538494Sobrien { S("arch"), 15638494Sobrien 0, &gopt.arch, 0, TRUE }, 15738494Sobrien { S("dev"), 15838494Sobrien &fs_static.opt_dev, 0, 0, FALSE }, 15938494Sobrien { S("pref"), 16038494Sobrien &fs_static.opt_pref, 0, 0, FALSE }, 16138494Sobrien { S("path"), 16238494Sobrien 0, &opt_path, 0, FALSE }, 16338494Sobrien { S("autodir"), 16438494Sobrien 0, &gopt.auto_dir, 0, FALSE }, 16538494Sobrien { S("delay"), 16638494Sobrien &fs_static.opt_delay, 0, 0, FALSE }, 16738494Sobrien { S("domain"), 16838494Sobrien 0, &hostdomain, 0, TRUE }, 16938494Sobrien { S("karch"), 17038494Sobrien 0, &gopt.karch, 0, TRUE }, 17138494Sobrien { S("cluster"), 17238494Sobrien 0, &gopt.cluster, 0, TRUE }, 17338494Sobrien { S("wire"), 17438494Sobrien 0, 0, f_in_network, TRUE }, 17538494Sobrien { S("network"), 17638494Sobrien 0, 0, f_in_network, TRUE }, 17738494Sobrien { S("netnumber"), 17838494Sobrien 0, 0, f_in_network, TRUE }, 17938494Sobrien { S("byte"), 18038494Sobrien 0, &endian, 0, TRUE }, 18138494Sobrien { S("os"), 18238494Sobrien 0, &gopt.op_sys, 0, TRUE }, 18338494Sobrien { S("osver"), 18438494Sobrien 0, &gopt.op_sys_ver, 0, TRUE }, 18552894Sobrien { S("full_os"), 18652894Sobrien 0, &gopt.op_sys_full, 0, TRUE }, 18752894Sobrien { S("vendor"), 18852894Sobrien 0, &gopt.op_sys_vendor, 0, TRUE }, 18938494Sobrien { S("remopts"), 19038494Sobrien &fs_static.opt_remopts, 0, 0, FALSE }, 19138494Sobrien { S("mount"), 19238494Sobrien &fs_static.opt_mount, 0, 0, FALSE }, 19338494Sobrien { S("unmount"), 19438494Sobrien &fs_static.opt_unmount, 0, 0, FALSE }, 195174294Sobrien { S("umount"), 196174294Sobrien &fs_static.opt_umount, 0, 0, FALSE }, 19738494Sobrien { S("cache"), 19838494Sobrien &fs_static.opt_cache, 0, 0, FALSE }, 19938494Sobrien { S("user"), 20038494Sobrien &fs_static.opt_user, 0, 0, FALSE }, 20138494Sobrien { S("group"), 20238494Sobrien &fs_static.opt_group, 0, 0, FALSE }, 20338494Sobrien { S(".key"), 20438494Sobrien 0, &opt_dkey, 0, FALSE }, 20538494Sobrien { S("key."), 20638494Sobrien 0, &opt_keyd, 0, FALSE }, 20738494Sobrien { S("maptype"), 20838494Sobrien &fs_static.opt_maptype, 0, 0, FALSE }, 20938494Sobrien { S("cachedir"), 21038494Sobrien &fs_static.opt_cachedir, 0, 0, FALSE }, 21138494Sobrien { S("addopts"), 21282794Sobrien &fs_static.opt_addopts, 0, 0, FALSE }, 21382794Sobrien { S("uid"), 21482794Sobrien 0, &opt_uid, 0, FALSE }, 21582794Sobrien { S("gid"), 21682794Sobrien 0, &opt_gid, 0, FALSE }, 217174294Sobrien { S("mount_type"), 218174294Sobrien &fs_static.opt_mount_type, 0, 0, FALSE }, 21982794Sobrien { S("dollar"), 22082794Sobrien &literal_dollar, 0, 0, FALSE }, 22138494Sobrien { S("var0"), 22238494Sobrien &vars[0], 0, 0, FALSE }, 22338494Sobrien { S("var1"), 22438494Sobrien &vars[1], 0, 0, FALSE }, 22538494Sobrien { S("var2"), 22638494Sobrien &vars[2], 0, 0, FALSE }, 22738494Sobrien { S("var3"), 22838494Sobrien &vars[3], 0, 0, FALSE }, 22938494Sobrien { S("var4"), 23038494Sobrien &vars[4], 0, 0, FALSE }, 23138494Sobrien { S("var5"), 23238494Sobrien &vars[5], 0, 0, FALSE }, 23338494Sobrien { S("var6"), 23438494Sobrien &vars[6], 0, 0, FALSE }, 23538494Sobrien { S("var7"), 23638494Sobrien &vars[7], 0, 0, FALSE }, 23738494Sobrien { 0, 0, 0, 0, 0, FALSE }, 23838494Sobrien}; 23938494Sobrien 24038494Sobrienstatic struct functable functable[] = { 24138494Sobrien { "in_network", f_in_network }, 242174294Sobrien { "xhost", f_xhost }, 24338494Sobrien { "netgrp", f_netgrp }, 24451292Sobrien { "netgrpd", f_netgrpd }, 24538494Sobrien { "exists", f_exists }, 24638494Sobrien { "false", f_false }, 24738494Sobrien { "true", f_true }, 24838494Sobrien { 0, 0 }, 24938494Sobrien}; 25038494Sobrien 25138494Sobrien/* 25238494Sobrien * Specially expand the remote host name first 25338494Sobrien */ 25438494Sobrienstatic opt_apply rhost_expansion[] = 25538494Sobrien{ 25638494Sobrien {&fs_static.opt_rhost, "${host}"}, 25738494Sobrien {0, 0}, 25838494Sobrien}; 25938494Sobrien 26038494Sobrien/* 26138494Sobrien * List of options which need to be expanded 26238494Sobrien * Note that the order here _may_ be important. 26338494Sobrien */ 26438494Sobrienstatic opt_apply expansions[] = 26538494Sobrien{ 26638494Sobrien {&fs_static.opt_sublink, 0}, 26738494Sobrien {&fs_static.opt_rfs, "${path}"}, 26838494Sobrien {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"}, 26938494Sobrien {&fs_static.opt_opts, "rw"}, 27038494Sobrien {&fs_static.opt_remopts, "${opts}"}, 27138494Sobrien {&fs_static.opt_mount, 0}, 27238494Sobrien {&fs_static.opt_unmount, 0}, 273174294Sobrien {&fs_static.opt_umount, 0}, 27438494Sobrien {&fs_static.opt_cachedir, 0}, 27538494Sobrien {&fs_static.opt_addopts, 0}, 27638494Sobrien {0, 0}, 27738494Sobrien}; 27838494Sobrien 27938494Sobrien/* 28038494Sobrien * List of options which need to be free'ed before re-use 28138494Sobrien */ 28238494Sobrienstatic opt_apply to_free[] = 28338494Sobrien{ 28438494Sobrien {&fs_static.fs_glob, 0}, 28538494Sobrien {&fs_static.fs_local, 0}, 28638494Sobrien {&fs_static.fs_mtab, 0}, 28738494Sobrien {&fs_static.opt_sublink, 0}, 28838494Sobrien {&fs_static.opt_rfs, 0}, 28938494Sobrien {&fs_static.opt_fs, 0}, 29038494Sobrien {&fs_static.opt_rhost, 0}, 29138494Sobrien {&fs_static.opt_opts, 0}, 29238494Sobrien {&fs_static.opt_remopts, 0}, 29338494Sobrien {&fs_static.opt_mount, 0}, 29438494Sobrien {&fs_static.opt_unmount, 0}, 295174294Sobrien {&fs_static.opt_umount, 0}, 29638494Sobrien {&fs_static.opt_cachedir, 0}, 29738494Sobrien {&fs_static.opt_addopts, 0}, 29838494Sobrien {&vars[0], 0}, 29938494Sobrien {&vars[1], 0}, 30038494Sobrien {&vars[2], 0}, 30138494Sobrien {&vars[3], 0}, 30238494Sobrien {&vars[4], 0}, 30338494Sobrien {&vars[5], 0}, 30438494Sobrien {&vars[6], 0}, 30538494Sobrien {&vars[7], 0}, 30638494Sobrien {0, 0}, 30738494Sobrien}; 30838494Sobrien 30938494Sobrien 31038494Sobrien/* 31138494Sobrien * expand backslash escape sequences 312174294Sobrien * (escaped slash is handled separately in normalize_slash) 31338494Sobrien */ 31438494Sobrienstatic char 31538494Sobrienbackslash(char **p) 31638494Sobrien{ 31738494Sobrien char c; 31838494Sobrien 31938494Sobrien if ((*p)[1] == '\0') { 32038494Sobrien plog(XLOG_USER, "Empty backslash escape"); 32138494Sobrien return **p; 32238494Sobrien } 32338494Sobrien 32438494Sobrien if (**p == '\\') { 32538494Sobrien (*p)++; 32638494Sobrien switch (**p) { 32782794Sobrien case 'g': 32838494Sobrien c = '\007'; /* Bell */ 32938494Sobrien break; 33038494Sobrien case 'b': 33138494Sobrien c = '\010'; /* Backspace */ 33238494Sobrien break; 33338494Sobrien case 't': 33438494Sobrien c = '\011'; /* Horizontal Tab */ 33538494Sobrien break; 33638494Sobrien case 'n': 33738494Sobrien c = '\012'; /* New Line */ 33838494Sobrien break; 33938494Sobrien case 'v': 34038494Sobrien c = '\013'; /* Vertical Tab */ 34138494Sobrien break; 34238494Sobrien case 'f': 34338494Sobrien c = '\014'; /* Form Feed */ 34438494Sobrien break; 34538494Sobrien case 'r': 34638494Sobrien c = '\015'; /* Carriage Return */ 34738494Sobrien break; 34838494Sobrien case 'e': 34938494Sobrien c = '\033'; /* Escape */ 35038494Sobrien break; 35138494Sobrien case '0': 35238494Sobrien case '1': 35338494Sobrien case '2': 35438494Sobrien case '3': 35538494Sobrien case '4': 35638494Sobrien case '5': 35738494Sobrien case '6': 35838494Sobrien case '7': 35938494Sobrien { 36038494Sobrien int cnt, val, ch; 36138494Sobrien 36238494Sobrien for (cnt = 0, val = 0; cnt < 3; cnt++) { 36338494Sobrien ch = *(*p)++; 36438494Sobrien if (ch < '0' || ch > '7') { 36538494Sobrien (*p)--; 36638494Sobrien break; 36738494Sobrien } 36838494Sobrien val = (val << 3) | (ch - '0'); 36938494Sobrien } 37038494Sobrien 37138494Sobrien if ((val & 0xffffff00) != 0) 37238494Sobrien plog(XLOG_USER, 37338494Sobrien "Too large character constant %u\n", 37438494Sobrien val); 37538494Sobrien c = (char) val; 37638494Sobrien --(*p); 37738494Sobrien } 37838494Sobrien break; 37938494Sobrien 38038494Sobrien default: 38138494Sobrien c = **p; 38238494Sobrien break; 38338494Sobrien } 38438494Sobrien } else 38538494Sobrien c = **p; 38638494Sobrien 38738494Sobrien return c; 38838494Sobrien} 38938494Sobrien 39038494Sobrien 39138494Sobrien/* 39238494Sobrien * Skip to next option in the string 39338494Sobrien */ 39438494Sobrienstatic char * 39538494Sobrienopt(char **p) 39638494Sobrien{ 39738494Sobrien char *cp = *p; 39838494Sobrien char *dp = cp; 39938494Sobrien char *s = cp; 40038494Sobrien 40138494Sobrientop: 40238494Sobrien while (*cp && *cp != ';') { 40338494Sobrien if (*cp == '"') { 40438494Sobrien /* 40538494Sobrien * Skip past string 40638494Sobrien */ 40738494Sobrien for (cp++; *cp && *cp != '"'; cp++) 40838494Sobrien if (*cp == '\\') 40938494Sobrien *dp++ = backslash(&cp); 41038494Sobrien else 41138494Sobrien *dp++ = *cp; 41238494Sobrien if (*cp) 41338494Sobrien cp++; 41438494Sobrien } else { 41538494Sobrien *dp++ = *cp++; 41638494Sobrien } 41738494Sobrien } 41838494Sobrien 41938494Sobrien /* 42038494Sobrien * Skip past any remaining ';'s 42138494Sobrien */ 42238494Sobrien while (*cp == ';') 42338494Sobrien cp++; 42438494Sobrien 42538494Sobrien /* 42638494Sobrien * If we have a zero length string 42738494Sobrien * and there are more fields, then 42838494Sobrien * parse the next one. This allows 42938494Sobrien * sequences of empty fields. 43038494Sobrien */ 43138494Sobrien if (*cp && dp == s) 43238494Sobrien goto top; 43338494Sobrien 43438494Sobrien *dp = '\0'; 43538494Sobrien 43638494Sobrien *p = cp; 43738494Sobrien return s; 43838494Sobrien} 43938494Sobrien 44038494Sobrien 44138494Sobrien/* 44238494Sobrien * These routines add a new style of selector; function-style boolean 44338494Sobrien * operators. To add new ones, just define functions as in true, false, 44438494Sobrien * exists (below) and add them to the functable, above. 44538494Sobrien * 44638494Sobrien * Usage example: Some people have X11R5 local, some go to a server. I do 44738494Sobrien * this: 44838494Sobrien * 44938494Sobrien * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \ 45038494Sobrien * -type:=nfs;rfs=/usr/pkg/${key} \ 45138494Sobrien * rhost:=server1 \ 45238494Sobrien * rhost:=server2 45338494Sobrien * 45438494Sobrien * -Rens Troost <rens@imsi.com> 45538494Sobrien */ 45638494Sobrienstatic IntFuncPtr 45738494Sobrienfunctable_lookup(char *key) 45838494Sobrien{ 45938494Sobrien struct functable *fp; 46038494Sobrien 46138494Sobrien for (fp = functable; fp->name; fp++) 46238494Sobrien if (FSTREQ(fp->name, key)) 46338494Sobrien return (fp->func); 46438494Sobrien return (IntFuncPtr) NULL; 46538494Sobrien} 46638494Sobrien 46738494Sobrien 468174294Sobrien/* 469174294Sobrien * Fill in the global structure fs_static by 470174294Sobrien * cracking the string opts. opts may be 471174294Sobrien * scribbled on at will. Does NOT evaluate options. 472174294Sobrien * Returns 0 on error, 1 if no syntax errors were discovered. 473174294Sobrien */ 47438494Sobrienstatic int 475174294Sobriensplit_opts(char *opts, char *mapkey) 47638494Sobrien{ 477174294Sobrien char *o = opts; 478174294Sobrien char *f; 479174294Sobrien 48038494Sobrien /* 481174294Sobrien * For each user-specified option 48238494Sobrien */ 483174294Sobrien for (f = opt(&o); *f; f = opt(&o)) { 484174294Sobrien struct opt *op; 485174294Sobrien char *eq = strchr(f, '='); 486174294Sobrien char *opt = NULL; 487174294Sobrien 488174294Sobrien if (!eq) 489174294Sobrien continue; 490174294Sobrien 491174294Sobrien if (*(eq-1) == '!' || 492174294Sobrien eq[1] == '=' || 493174294Sobrien eq[1] == '!') { /* != or == or =! */ 494174294Sobrien continue; /* we don't care about selectors */ 495174294Sobrien } 496174294Sobrien 497174294Sobrien if (*(eq-1) == ':') { /* := */ 498174294Sobrien *(eq-1) = '\0'; 499174294Sobrien } else { 500174294Sobrien /* old style assignment */ 501174294Sobrien eq[0] = '\0'; 502174294Sobrien } 503174294Sobrien opt = eq + 1; 504174294Sobrien 505174294Sobrien /* 506174294Sobrien * For each recognized option 507174294Sobrien */ 508174294Sobrien for (op = opt_fields; op->name; op++) { 509174294Sobrien /* 510174294Sobrien * Check whether they match 511174294Sobrien */ 512174294Sobrien if (FSTREQ(op->name, f)) { 513174294Sobrien if (op->sel_p) { 514174294Sobrien plog(XLOG_USER, "key %s: Can't assign to a selector (%s)", 515174294Sobrien mapkey, op->name); 516174294Sobrien return 0; 517174294Sobrien } 518174294Sobrien *op->optp = opt; /* actual assignment into fs_static */ 519174294Sobrien break; /* break out of for loop */ 520174294Sobrien } /* end of "if (FSTREQ(op->name, f))" statement */ 521174294Sobrien } /* end of "for (op = opt_fields..." statement */ 522174294Sobrien 523174294Sobrien if (!op->name) 524174294Sobrien plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f); 525174294Sobrien } 526174294Sobrien 527174294Sobrien return 1; 528174294Sobrien} 529174294Sobrien 530174294Sobrien 531174294Sobrien/* 532174294Sobrien * Just evaluate selectors, which were split by split_opts. 533174294Sobrien * Returns 0 on error or no match, 1 if matched. 534174294Sobrien */ 535174294Sobrienstatic int 536174294Sobrieneval_selectors(char *opts, char *mapkey) 537174294Sobrien{ 538174294Sobrien char *o, *old_o; 53938494Sobrien char *f; 540174294Sobrien int ret = 0; 54138494Sobrien 542174294Sobrien o = old_o = strdup(opts); 543174294Sobrien 54438494Sobrien /* 54538494Sobrien * For each user-specified option 54638494Sobrien */ 547174294Sobrien for (f = opt(&o); *f; f = opt(&o)) { 54838494Sobrien struct opt *op; 549174294Sobrien enum vs_opt vs_opt; 55038494Sobrien char *eq = strchr(f, '='); 55138494Sobrien char *fx; 55238494Sobrien IntFuncPtr func; 55338494Sobrien char *opt = NULL; 554174294Sobrien char *arg; 55538494Sobrien 556174294Sobrien if (!eq) { 55738494Sobrien /* 55838494Sobrien * No value, is it a function call? 55938494Sobrien */ 560174294Sobrien arg = strchr(f, '('); 56138494Sobrien 56238494Sobrien if (!arg || arg[1] == '\0' || arg == f) { 56338494Sobrien /* 56438494Sobrien * No, just continue 56538494Sobrien */ 56638494Sobrien plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f); 56738494Sobrien continue; 56838494Sobrien } 56938494Sobrien 57038494Sobrien /* null-terminate the argument */ 57138494Sobrien *arg++ = '\0'; 57238494Sobrien fx = strchr(arg, ')'); 57338494Sobrien if (!arg || fx == arg) { 57438494Sobrien plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f); 57538494Sobrien continue; 57638494Sobrien } 57738494Sobrien *fx = '\0'; 57838494Sobrien 579174294Sobrien if (f[0] == '!') { 580174294Sobrien vs_opt = SelNE; 581174294Sobrien f++; 582174294Sobrien } else { 583174294Sobrien vs_opt = SelEQ; 584174294Sobrien } 58538494Sobrien /* 58638494Sobrien * look up f in functable and pass it arg. 58738494Sobrien * func must return 0 on failure, and 1 on success. 58838494Sobrien */ 58938494Sobrien if ((func = functable_lookup(f))) { 590174294Sobrien int funok; 591174294Sobrien 592174294Sobrien /* this allocates memory, don't forget to free */ 593174294Sobrien arg = expand_options(arg); 594174294Sobrien funok = func(arg); 595174294Sobrien XFREE(arg); 596174294Sobrien 597174294Sobrien if (vs_opt == SelNE) 598174294Sobrien funok = !funok; 599174294Sobrien if (!funok) 600174294Sobrien goto out; 601174294Sobrien 60238494Sobrien continue; 60338494Sobrien } else { 60438494Sobrien plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f); 605174294Sobrien goto out; 60638494Sobrien } 607174294Sobrien } else { 608174294Sobrien if (eq[1] == '\0' || eq == f) { 609174294Sobrien /* misformed selector */ 610174294Sobrien plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f); 611174294Sobrien continue; 612174294Sobrien } 61338494Sobrien } 61438494Sobrien 61538494Sobrien /* 61638494Sobrien * Check what type of operation is happening 61738494Sobrien * !=, =! is SelNE 61838494Sobrien * == is SelEQ 619174294Sobrien * =, := is VarAss 62038494Sobrien */ 621174294Sobrien if (*(eq-1) == '!') { /* != */ 62238494Sobrien vs_opt = SelNE; 623174294Sobrien *(eq-1) = '\0'; 62438494Sobrien opt = eq + 1; 625174294Sobrien } else if (*(eq-1) == ':') { /* := */ 626174294Sobrien continue; 62738494Sobrien } else if (eq[1] == '=') { /* == */ 62838494Sobrien vs_opt = SelEQ; 62938494Sobrien eq[0] = '\0'; 63038494Sobrien opt = eq + 2; 63138494Sobrien } else if (eq[1] == '!') { /* =! */ 63238494Sobrien vs_opt = SelNE; 63338494Sobrien eq[0] = '\0'; 63438494Sobrien opt = eq + 2; 635174294Sobrien } else { 636174294Sobrien /* old style assignment */ 637174294Sobrien continue; 63838494Sobrien } 63938494Sobrien 64038494Sobrien /* 64138494Sobrien * For each recognized option 64238494Sobrien */ 64338494Sobrien for (op = opt_fields; op->name; op++) { 64438494Sobrien /* 64538494Sobrien * Check whether they match 64638494Sobrien */ 64738494Sobrien if (FSTREQ(op->name, f)) { 648174294Sobrien opt = expand_options(opt); 649174294Sobrien 650174294Sobrien if (op->sel_p != NULL) { 651174294Sobrien int selok; 652174294Sobrien if (op->case_insensitive) { 653174294Sobrien selok = STRCEQ(*op->sel_p, opt); 654174294Sobrien } else { 655174294Sobrien selok = STREQ(*op->sel_p, opt); 656174294Sobrien } 657174294Sobrien if (vs_opt == SelNE) 658174294Sobrien selok = !selok; 659174294Sobrien if (!selok) { 66038494Sobrien plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s", 66138494Sobrien mapkey, 66238494Sobrien op->name, 66338494Sobrien *op->sel_p, 66438494Sobrien vs_opt == SelNE ? "mis" : "", 66538494Sobrien opt); 666174294Sobrien XFREE(opt); 667174294Sobrien goto out; 66838494Sobrien } 669174294Sobrien XFREE(opt); 670174294Sobrien } 671174294Sobrien /* check if to apply a function */ 672174294Sobrien if (op->fxn_p) { 673174294Sobrien int funok; 674174294Sobrien 675174294Sobrien funok = op->fxn_p(opt); 676174294Sobrien if (vs_opt == SelNE) 677174294Sobrien funok = !funok; 678174294Sobrien if (!funok) { 67938494Sobrien plog(XLOG_MAP, "key %s: map function %s did not %smatch %s", 68038494Sobrien mapkey, 68138494Sobrien op->name, 68238494Sobrien vs_opt == SelNE ? "mis" : "", 68338494Sobrien opt); 684174294Sobrien XFREE(opt); 685174294Sobrien goto out; 68638494Sobrien } 687174294Sobrien XFREE(opt); 688174294Sobrien } 68938494Sobrien break; /* break out of for loop */ 69038494Sobrien } 69138494Sobrien } 69238494Sobrien 69338494Sobrien if (!op->name) 69438494Sobrien plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f); 69538494Sobrien } 69638494Sobrien 697174294Sobrien /* all is ok */ 698174294Sobrien ret = 1; 699174294Sobrien 700174294Sobrien out: 701174294Sobrien free(old_o); 702174294Sobrien return ret; 70338494Sobrien} 70438494Sobrien 70538494Sobrien 70638494Sobrien/* 70738494Sobrien * Skip to next option in the string, but don't scribble over the string. 70838494Sobrien * However, *p gets repointed to the start of the next string past ';'. 70938494Sobrien */ 71038494Sobrienstatic char * 71138494Sobrienopt_no_scribble(char **p) 71238494Sobrien{ 71338494Sobrien char *cp = *p; 71438494Sobrien char *dp = cp; 71538494Sobrien char *s = cp; 71638494Sobrien 71738494Sobrientop: 71838494Sobrien while (*cp && *cp != ';') { 71938494Sobrien if (*cp == '\"') { 72038494Sobrien /* 72138494Sobrien * Skip past string 72238494Sobrien */ 72338494Sobrien cp++; 72438494Sobrien while (*cp && *cp != '\"') 72538494Sobrien *dp++ = *cp++; 72638494Sobrien if (*cp) 72738494Sobrien cp++; 72838494Sobrien } else { 72938494Sobrien *dp++ = *cp++; 73038494Sobrien } 73138494Sobrien } 73238494Sobrien 73338494Sobrien /* 73438494Sobrien * Skip past any remaining ';'s 73538494Sobrien */ 73638494Sobrien while (*cp == ';') 73738494Sobrien cp++; 73838494Sobrien 73938494Sobrien /* 74038494Sobrien * If we have a zero length string 74138494Sobrien * and there are more fields, then 74238494Sobrien * parse the next one. This allows 74338494Sobrien * sequences of empty fields. 74438494Sobrien */ 74538494Sobrien if (*cp && dp == s) 74638494Sobrien goto top; 74738494Sobrien 74838494Sobrien *p = cp; 74938494Sobrien return s; 75038494Sobrien} 75138494Sobrien 75238494Sobrien 75338494Sobrien/* 75438494Sobrien * Strip any selectors from a string. Selectors are all assumed to be 75538494Sobrien * first in the string. This is used for the new /defaults method which will 75638494Sobrien * use selectors as well. 75738494Sobrien */ 75838494Sobrienchar * 75938494Sobrienstrip_selectors(char *opts, char *mapkey) 76038494Sobrien{ 76138494Sobrien /* 76238494Sobrien * Fill in the global structure fs_static by 76338494Sobrien * cracking the string opts. opts may be 76438494Sobrien * scribbled on at will. 76538494Sobrien */ 76638494Sobrien char *o = opts; 76738494Sobrien char *oo = opts; 76838494Sobrien char *f; 76938494Sobrien 77038494Sobrien /* 77138494Sobrien * Scan options. Note that the opt() function scribbles on the opt string. 77238494Sobrien */ 77338494Sobrien while (*(f = opt_no_scribble(&o))) { 77438494Sobrien enum vs_opt vs_opt = VarAss; 77538494Sobrien char *eq = strchr(f, '='); 77638494Sobrien 77738494Sobrien if (!eq || eq[1] == '\0' || eq == f) { 77838494Sobrien /* 77938494Sobrien * No option or assignment? Return as is. 78038494Sobrien */ 78138494Sobrien plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f); 78238494Sobrien return o; 78338494Sobrien } 78438494Sobrien /* 78538494Sobrien * Check what type of operation is happening 78638494Sobrien * !=, =! is SelNE 78738494Sobrien * == is SelEQ 78838494Sobrien * := is VarAss 78938494Sobrien */ 790174294Sobrien if (*(eq-1) == '!') { /* != */ 79138494Sobrien vs_opt = SelNE; 792174294Sobrien } else if (*(eq-1) == ':') { /* := */ 79338494Sobrien vs_opt = VarAss; 79438494Sobrien } else if (eq[1] == '=') { /* == */ 79538494Sobrien vs_opt = SelEQ; 79638494Sobrien } else if (eq[1] == '!') { /* =! */ 79738494Sobrien vs_opt = SelNE; 79838494Sobrien } 79938494Sobrien switch (vs_opt) { 80038494Sobrien case SelEQ: 80138494Sobrien case SelNE: 80238494Sobrien /* Skip this selector, maybe there's another one following it */ 80338494Sobrien plog(XLOG_USER, "skipping selector to \"%s\"", o); 80438494Sobrien /* store previous match. it may have been the first assignment */ 80538494Sobrien oo = o; 80638494Sobrien break; 80738494Sobrien 80838494Sobrien case VarAss: 80938494Sobrien /* found the first assignment, return the string starting with it */ 81038494Sobrien dlog("found first assignment past selectors \"%s\"", o); 81138494Sobrien return oo; 81238494Sobrien } 81338494Sobrien } 81438494Sobrien 81538494Sobrien /* return the same string by default. should not happen. */ 81638494Sobrien return oo; 81738494Sobrien} 81838494Sobrien 81938494Sobrien 82038494Sobrien/***************************************************************************** 82138494Sobrien *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): *** 82238494Sobrien *****************************************************************************/ 82338494Sobrien 82438494Sobrien/* test if arg is any of this host's network names or numbers */ 82538494Sobrienstatic int 82638494Sobrienf_in_network(char *arg) 82738494Sobrien{ 82838494Sobrien int status; 82938494Sobrien 83038494Sobrien if (!arg) 831174294Sobrien return 0; 83238494Sobrien 83338494Sobrien status = is_network_member(arg); 834174294Sobrien dlog("%s is %son a local network", arg, (status ? "" : "not ")); 83538494Sobrien return status; 83638494Sobrien} 83738494Sobrien 83838494Sobrien 839174294Sobrien/* 840174294Sobrien * Test if arg is any of this host's names or aliases (CNAMES). 841174294Sobrien * Note: this function compares against the fully expanded host name (hostd). 842174294Sobrien * XXX: maybe we also need to compare against the stripped host name? 843174294Sobrien */ 844174294Sobrienstatic int 845174294Sobrienf_xhost(char *arg) 846174294Sobrien{ 847174294Sobrien struct hostent *hp; 848174294Sobrien char **cp; 849174294Sobrien 850174294Sobrien if (!arg) 851174294Sobrien return 0; 852174294Sobrien 853174294Sobrien /* simple test: does it match main host name? */ 854174294Sobrien if (STREQ(arg, opt_hostd)) 855174294Sobrien return 1; 856174294Sobrien 857174294Sobrien /* now find all of the names of "arg" and compare against opt_hostd */ 858174294Sobrien hp = gethostbyname(arg); 859174294Sobrien if (hp == NULL) { 860174294Sobrien#ifdef HAVE_HSTRERROR 861174294Sobrien plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno)); 862174294Sobrien#else /* not HAVE_HSTRERROR */ 863174294Sobrien plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno); 864174294Sobrien#endif /* not HAVE_HSTRERROR */ 865174294Sobrien return 0; 866174294Sobrien } 867174294Sobrien /* check primary name */ 868174294Sobrien if (hp->h_name) { 869174294Sobrien dlog("xhost: compare %s==%s", hp->h_name, opt_hostd); 870174294Sobrien if (STREQ(hp->h_name, opt_hostd)) { 871174294Sobrien plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name); 872174294Sobrien return 1; 873174294Sobrien } 874174294Sobrien } 875174294Sobrien /* check all aliases, if any */ 876174294Sobrien if (hp->h_aliases == NULL) { 877174294Sobrien dlog("gethostbyname(%s) has no aliases", arg); 878174294Sobrien return 0; 879174294Sobrien } 880174294Sobrien cp = hp->h_aliases; 881174294Sobrien while (*cp) { 882174294Sobrien dlog("xhost: compare alias %s==%s", *cp, opt_hostd); 883174294Sobrien if (STREQ(*cp, opt_hostd)) { 884174294Sobrien plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp); 885174294Sobrien return 1; 886174294Sobrien } 887174294Sobrien cp++; 888174294Sobrien } 889174294Sobrien /* nothing matched */ 890174294Sobrien return 0; 891174294Sobrien} 892174294Sobrien 893174294Sobrien 89451292Sobrien/* test if this host (short hostname form) is in netgroup (arg) */ 89538494Sobrienstatic int 89638494Sobrienf_netgrp(char *arg) 89738494Sobrien{ 89838494Sobrien int status; 899174294Sobrien char *ptr, *nhost; 90038494Sobrien 901174294Sobrien if ((ptr = strchr(arg, ',')) != NULL) { 902174294Sobrien *ptr = '\0'; 903174294Sobrien nhost = ptr + 1; 904174294Sobrien } else { 905174294Sobrien nhost = opt_host; 906174294Sobrien } 907174294Sobrien status = innetgr(arg, nhost, NULL, NULL); 908174294Sobrien dlog("netgrp = %s status = %d host = %s", arg, status, nhost); 909174294Sobrien if (ptr) 910174294Sobrien *ptr = ','; 91138494Sobrien return status; 91238494Sobrien} 91338494Sobrien 91438494Sobrien 91551292Sobrien/* test if this host (fully-qualified name) is in netgroup (arg) */ 91651292Sobrienstatic int 91751292Sobrienf_netgrpd(char *arg) 91851292Sobrien{ 91951292Sobrien int status; 920174294Sobrien char *ptr, *nhost; 92151292Sobrien 922174294Sobrien if ((ptr = strchr(arg, ',')) != NULL) { 923174294Sobrien *ptr = '\0'; 924174294Sobrien nhost = ptr + 1; 925174294Sobrien } else { 926174294Sobrien nhost = opt_hostd; 927174294Sobrien } 928174294Sobrien status = innetgr(arg, nhost, NULL, NULL); 929174294Sobrien dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost); 930174294Sobrien if (ptr) 931174294Sobrien *ptr = ','; 93251292Sobrien return status; 93351292Sobrien} 93451292Sobrien 93551292Sobrien 93638494Sobrien/* test if file (arg) exists via lstat */ 93738494Sobrienstatic int 93838494Sobrienf_exists(char *arg) 93938494Sobrien{ 94038494Sobrien struct stat buf; 94138494Sobrien 94238494Sobrien if (lstat(arg, &buf) < 0) 94338494Sobrien return (0); 94438494Sobrien else 94538494Sobrien return (1); 94638494Sobrien} 94738494Sobrien 94838494Sobrien 94938494Sobrien/* always false */ 95038494Sobrienstatic int 95138494Sobrienf_false(char *arg) 95238494Sobrien{ 95338494Sobrien return (0); 95438494Sobrien} 95538494Sobrien 95638494Sobrien 95738494Sobrien/* always true */ 95838494Sobrienstatic int 95938494Sobrienf_true(char *arg) 96038494Sobrien{ 96138494Sobrien return (1); 96238494Sobrien} 96338494Sobrien 96438494Sobrien 96538494Sobrien/* 96638494Sobrien * Free an option 96738494Sobrien */ 96838494Sobrienstatic void 96938494Sobrienfree_op(opt_apply *p, int b) 97038494Sobrien{ 97138494Sobrien if (*p->opt) { 97238494Sobrien XFREE(*p->opt); 97338494Sobrien } 97438494Sobrien} 97538494Sobrien 97638494Sobrien 97738494Sobrien/* 97838494Sobrien * Normalize slashes in the string. 97938494Sobrien */ 98038494Sobrienvoid 98138494Sobriennormalize_slash(char *p) 98238494Sobrien{ 983174294Sobrien char *f, *f0; 98438494Sobrien 985174294Sobrien if (!(gopt.flags & CFM_NORMALIZE_SLASHES)) 986174294Sobrien return; 987174294Sobrien 988174294Sobrien f0 = f = strchr(p, '/'); 98938494Sobrien if (f) { 99038494Sobrien char *t = f; 99138494Sobrien do { 99238494Sobrien /* assert(*f == '/'); */ 99338494Sobrien if (f == f0 && f[0] == '/' && f[1] == '/') { 99438494Sobrien /* copy double slash iff first */ 99538494Sobrien *t++ = *f++; 99638494Sobrien *t++ = *f++; 99738494Sobrien } else { 99838494Sobrien /* copy a single / across */ 99938494Sobrien *t++ = *f++; 100038494Sobrien } 100138494Sobrien 100238494Sobrien /* assert(f[-1] == '/'); */ 100338494Sobrien /* skip past more /'s */ 100438494Sobrien while (*f == '/') 100538494Sobrien f++; 100638494Sobrien 100738494Sobrien /* assert(*f != '/'); */ 100838494Sobrien /* keep copying up to next / */ 100938494Sobrien while (*f && *f != '/') { 1010174294Sobrien /* support escaped slashes '\/' */ 1011174294Sobrien if (f[0] == '\\' && f[1] == '/') 1012174294Sobrien f++; /* skip backslash */ 101338494Sobrien *t++ = *f++; 101438494Sobrien } 101538494Sobrien 101638494Sobrien /* assert(*f == 0 || *f == '/'); */ 101738494Sobrien 101838494Sobrien } while (*f); 101938494Sobrien *t = 0; /* derived from fix by Steven Glassman */ 102038494Sobrien } 102138494Sobrien} 102238494Sobrien 102338494Sobrien 102438494Sobrien/* 102538494Sobrien * Macro-expand an option. Note that this does not 102638494Sobrien * handle recursive expansions. They will go badly wrong. 1027174294Sobrien * If sel_p is true then old expand selectors, otherwise 102838494Sobrien * don't expand selectors. 102938494Sobrien */ 1030174294Sobrienstatic char * 1031174294Sobrienexpand_op(char *opt, int sel_p) 103238494Sobrien{ 1033174294Sobrien#define EXPAND_ERROR "No space to expand \"%s\"" 103438494Sobrien char expbuf[MAXPATHLEN + 1]; 103538494Sobrien char nbuf[NLEN + 1]; 103638494Sobrien char *ep = expbuf; 1037174294Sobrien char *cp = opt; 103838494Sobrien char *dp; 103938494Sobrien struct opt *op; 1040174294Sobrien char *cp_orig = opt; 104138494Sobrien 104238494Sobrien while ((dp = strchr(cp, '$'))) { 104338494Sobrien char ch; 104438494Sobrien /* 104538494Sobrien * First copy up to the $ 104638494Sobrien */ 104738494Sobrien { 104838494Sobrien int len = dp - cp; 104938494Sobrien 1050174294Sobrien if (len > 0) { 1051174294Sobrien if (BUFSPACE(ep, len)) { 1052174294Sobrien /* 1053174294Sobrien * We use strncpy (not xstrlcpy) because 'ep' relies on its 1054174294Sobrien * semantics. BUFSPACE guarantees that ep can hold len. 1055174294Sobrien */ 1056174294Sobrien strncpy(ep, cp, len); 1057174294Sobrien ep += len; 1058174294Sobrien } else { 1059174294Sobrien plog(XLOG_ERROR, EXPAND_ERROR, opt); 1060174294Sobrien goto out; 1061174294Sobrien } 106238494Sobrien } 106338494Sobrien } 106438494Sobrien 106538494Sobrien cp = dp + 1; 106638494Sobrien ch = *cp++; 106738494Sobrien if (ch == '$') { 106838494Sobrien if (BUFSPACE(ep, 1)) { 106938494Sobrien *ep++ = '$'; 107038494Sobrien } else { 1071174294Sobrien plog(XLOG_ERROR, EXPAND_ERROR, opt); 107238494Sobrien goto out; 107338494Sobrien } 107438494Sobrien } else if (ch == '{') { 107538494Sobrien /* Expansion... */ 107638494Sobrien enum { 107738494Sobrien E_All, E_Dir, E_File, E_Domain, E_Host 107838494Sobrien } todo; 107938494Sobrien /* 108038494Sobrien * Find closing brace 108138494Sobrien */ 108238494Sobrien char *br_p = strchr(cp, '}'); 108338494Sobrien int len; 108438494Sobrien 108538494Sobrien /* 108638494Sobrien * Check we found it 108738494Sobrien */ 108838494Sobrien if (!br_p) { 108938494Sobrien /* 109038494Sobrien * Just give up 109138494Sobrien */ 1092174294Sobrien plog(XLOG_USER, "No closing '}' in \"%s\"", opt); 109338494Sobrien goto out; 109438494Sobrien } 109538494Sobrien len = br_p - cp; 109638494Sobrien 109738494Sobrien /* 109838494Sobrien * Figure out which part of the variable to grab. 109938494Sobrien */ 110038494Sobrien if (*cp == '/') { 110138494Sobrien /* 110238494Sobrien * Just take the last component 110338494Sobrien */ 110438494Sobrien todo = E_File; 110538494Sobrien cp++; 110638494Sobrien --len; 1107174294Sobrien } else if (*(br_p-1) == '/') { 110838494Sobrien /* 110938494Sobrien * Take all but the last component 111038494Sobrien */ 111138494Sobrien todo = E_Dir; 111238494Sobrien --len; 111338494Sobrien } else if (*cp == '.') { 111438494Sobrien /* 111538494Sobrien * Take domain name 111638494Sobrien */ 111738494Sobrien todo = E_Domain; 111838494Sobrien cp++; 111938494Sobrien --len; 1120174294Sobrien } else if (*(br_p-1) == '.') { 112138494Sobrien /* 112238494Sobrien * Take host name 112338494Sobrien */ 112438494Sobrien todo = E_Host; 112538494Sobrien --len; 112638494Sobrien } else { 112738494Sobrien /* 112838494Sobrien * Take the whole lot 112938494Sobrien */ 113038494Sobrien todo = E_All; 113138494Sobrien } 113238494Sobrien 113338494Sobrien /* 113438494Sobrien * Truncate if too long. Since it won't 113538494Sobrien * match anyway it doesn't matter that 113638494Sobrien * it has been cut short. 113738494Sobrien */ 113838494Sobrien if (len > NLEN) 113938494Sobrien len = NLEN; 114038494Sobrien 114138494Sobrien /* 114238494Sobrien * Put the string into another buffer so 114338494Sobrien * we can do comparisons. 1144174294Sobrien * 1145174294Sobrien * We use strncpy here (not xstrlcpy) because the dest is meant 1146174294Sobrien * to be truncated and we don't want to log it as an error. The 1147174294Sobrien * use of the BUFSPACE macro above guarantees the safe use of 1148174294Sobrien * strncpy with nbuf. 114938494Sobrien */ 115038494Sobrien strncpy(nbuf, cp, len); 115138494Sobrien nbuf[len] = '\0'; 115238494Sobrien 115338494Sobrien /* 115438494Sobrien * Advance cp 115538494Sobrien */ 115638494Sobrien cp = br_p + 1; 115738494Sobrien 115838494Sobrien /* 115938494Sobrien * Search the option array 116038494Sobrien */ 116138494Sobrien for (op = opt_fields; op->name; op++) { 116238494Sobrien /* 116338494Sobrien * Check for match 116438494Sobrien */ 116538494Sobrien if (len == op->nlen && STREQ(op->name, nbuf)) { 116638494Sobrien char xbuf[NLEN + 3]; 116738494Sobrien char *val; 116838494Sobrien /* 116938494Sobrien * Found expansion. Copy 117038494Sobrien * the correct value field. 117138494Sobrien */ 117238494Sobrien if (!(!op->sel_p == !sel_p)) { 117338494Sobrien /* 117438494Sobrien * Copy the string across unexpanded 117538494Sobrien */ 1176174294Sobrien xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}", 1177174294Sobrien todo == E_File ? "/" : 1178174294Sobrien todo == E_Domain ? "." : "", 1179174294Sobrien nbuf, 1180174294Sobrien todo == E_Dir ? "/" : 1181174294Sobrien todo == E_Host ? "." : ""); 118238494Sobrien val = xbuf; 118338494Sobrien /* 118438494Sobrien * Make sure expansion doesn't 118538494Sobrien * munge the value! 118638494Sobrien */ 118738494Sobrien todo = E_All; 118838494Sobrien } else if (op->sel_p) { 118938494Sobrien val = *op->sel_p; 119038494Sobrien } else { 119138494Sobrien val = *op->optp; 119238494Sobrien } 119338494Sobrien 119438494Sobrien if (val) { 119538494Sobrien /* 119638494Sobrien * Do expansion: 119738494Sobrien * ${/var} means take just the last part 119838494Sobrien * ${var/} means take all but the last part 119938494Sobrien * ${.var} means take all but first part 120038494Sobrien * ${var.} means take just the first part 120138494Sobrien * ${var} means take the whole lot 120238494Sobrien */ 120338494Sobrien int vlen = strlen(val); 120438494Sobrien char *vptr = val; 120538494Sobrien switch (todo) { 120638494Sobrien case E_Dir: 120738494Sobrien vptr = strrchr(val, '/'); 120838494Sobrien if (vptr) 120938494Sobrien vlen = vptr - val; 121038494Sobrien vptr = val; 121138494Sobrien break; 121238494Sobrien case E_File: 121338494Sobrien vptr = strrchr(val, '/'); 121438494Sobrien if (vptr) { 121538494Sobrien vptr++; 121638494Sobrien vlen = strlen(vptr); 121738494Sobrien } else 121838494Sobrien vptr = val; 121938494Sobrien break; 122038494Sobrien case E_Domain: 122138494Sobrien vptr = strchr(val, '.'); 122238494Sobrien if (vptr) { 122338494Sobrien vptr++; 122438494Sobrien vlen = strlen(vptr); 122538494Sobrien } else { 122638494Sobrien vptr = ""; 122738494Sobrien vlen = 0; 122838494Sobrien } 122938494Sobrien break; 123038494Sobrien case E_Host: 123138494Sobrien vptr = strchr(val, '.'); 123238494Sobrien if (vptr) 123338494Sobrien vlen = vptr - val; 123438494Sobrien vptr = val; 123538494Sobrien break; 123638494Sobrien case E_All: 123738494Sobrien break; 123838494Sobrien } 123938494Sobrien 1240174294Sobrien if (BUFSPACE(ep, vlen+1)) { 1241174294Sobrien xstrlcpy(ep, vptr, vlen+1); 124238494Sobrien ep += vlen; 124338494Sobrien } else { 1244174294Sobrien plog(XLOG_ERROR, EXPAND_ERROR, opt); 124538494Sobrien goto out; 124638494Sobrien } 124738494Sobrien } 124838494Sobrien /* 124938494Sobrien * Done with this variable 125038494Sobrien */ 125138494Sobrien break; 125238494Sobrien } 125338494Sobrien } 125438494Sobrien 125538494Sobrien /* 125642629Sobrien * Check that the search was successful 125738494Sobrien */ 125838494Sobrien if (!op->name) { 125938494Sobrien /* 126038494Sobrien * If it wasn't then scan the 126138494Sobrien * environment for that name 126238494Sobrien * and use any value found 126338494Sobrien */ 126438494Sobrien char *env = getenv(nbuf); 126538494Sobrien 126638494Sobrien if (env) { 126738494Sobrien int vlen = strlen(env); 126838494Sobrien 1269174294Sobrien if (BUFSPACE(ep, vlen+1)) { 1270174294Sobrien xstrlcpy(ep, env, vlen+1); 127138494Sobrien ep += vlen; 127238494Sobrien } else { 1273174294Sobrien plog(XLOG_ERROR, EXPAND_ERROR, opt); 127438494Sobrien goto out; 127538494Sobrien } 1276174294Sobrien if (amuDebug(D_STR)) 127738494Sobrien plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env); 127838494Sobrien } else { 127938494Sobrien plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf); 128038494Sobrien } 128138494Sobrien } 128238494Sobrien } else { 128338494Sobrien /* 128438494Sobrien * Error, error 128538494Sobrien */ 1286174294Sobrien plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt); 128738494Sobrien } 128838494Sobrien } 128938494Sobrien 129038494Sobrienout: 129138494Sobrien /* 129238494Sobrien * Handle common case - no expansion 129338494Sobrien */ 1294174294Sobrien if (cp == opt) { 1295174294Sobrien opt = strdup(cp); 129638494Sobrien } else { 129738494Sobrien /* 129838494Sobrien * Finish off the expansion 129938494Sobrien */ 1300174294Sobrien int vlen = strlen(cp); 1301174294Sobrien if (BUFSPACE(ep, vlen+1)) { 1302174294Sobrien xstrlcpy(ep, cp, vlen+1); 1303174294Sobrien /* ep += vlen; */ 130438494Sobrien } else { 1305174294Sobrien plog(XLOG_ERROR, EXPAND_ERROR, opt); 130638494Sobrien } 130738494Sobrien 130838494Sobrien /* 130942629Sobrien * Save the expansion 131038494Sobrien */ 1311174294Sobrien opt = strdup(expbuf); 131238494Sobrien } 131338494Sobrien 1314174294Sobrien normalize_slash(opt); 131538494Sobrien 1316174294Sobrien if (amuDebug(D_STR)) { 131738494Sobrien plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig); 1318174294Sobrien plog(XLOG_DEBUG, "......... is \"%s\"", opt); 131938494Sobrien } 1320174294Sobrien return opt; 132138494Sobrien} 132238494Sobrien 132338494Sobrien 132438494Sobrien/* 132538494Sobrien * Wrapper for expand_op 132638494Sobrien */ 132738494Sobrienstatic void 132838494Sobrienexpand_opts(opt_apply *p, int sel_p) 132938494Sobrien{ 133038494Sobrien if (*p->opt) { 1331174294Sobrien *p->opt = expand_op(*p->opt, sel_p); 133238494Sobrien } else if (p->val) { 133338494Sobrien /* 133438494Sobrien * Do double expansion, remembering 133538494Sobrien * to free the string from the first 133638494Sobrien * expansion... 133738494Sobrien */ 1338174294Sobrien char *s = expand_op(p->val, TRUE); 1339174294Sobrien *p->opt = expand_op(s, sel_p); 134038494Sobrien XFREE(s); 134138494Sobrien } 134238494Sobrien} 134338494Sobrien 134438494Sobrien 134538494Sobrien/* 134638494Sobrien * Apply a function to a list of options 134738494Sobrien */ 134838494Sobrienstatic void 134938494Sobrienapply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b) 135038494Sobrien{ 135138494Sobrien opt_apply *pp; 135238494Sobrien 135338494Sobrien for (pp = ppp; pp->opt; pp++) 135438494Sobrien (*op) (pp, b); 135538494Sobrien} 135638494Sobrien 135738494Sobrien 135838494Sobrien/* 135938494Sobrien * Free the option table 136038494Sobrien */ 136138494Sobrienvoid 136238494Sobrienfree_opts(am_opts *fo) 136338494Sobrien{ 136438494Sobrien /* 136538494Sobrien * Copy in the structure we are playing with 136638494Sobrien */ 136738494Sobrien fs_static = *fo; 136838494Sobrien 136938494Sobrien /* 137038494Sobrien * Free previously allocated memory 137138494Sobrien */ 137238494Sobrien apply_opts(free_op, to_free, FALSE); 137338494Sobrien} 137438494Sobrien 137538494Sobrien 137638494Sobrien/* 1377174294Sobrien * Expand selectors (variables that cannot be assigned to or overridden) 137838494Sobrien */ 137938494Sobrienchar * 1380174294Sobrienexpand_selectors(char *key) 138138494Sobrien{ 1382174294Sobrien return expand_op(key, TRUE); 1383174294Sobrien} 138438494Sobrien 138538494Sobrien 1386174294Sobrien/* 1387174294Sobrien * Expand options (i.e. non-selectors, see above for definition) 1388174294Sobrien */ 1389174294Sobrienstatic inline char * 1390174294Sobrienexpand_options(char *key) 1391174294Sobrien{ 1392174294Sobrien return expand_op(key, FALSE); 139338494Sobrien} 139438494Sobrien 139538494Sobrien 139638494Sobrien/* 139738494Sobrien * Remove trailing /'s from a string 139838494Sobrien * unless the string is a single / (Steven Glassman) 139938494Sobrien * or unless it is two slashes // (Kevin D. Bond) 1400174294Sobrien * or unless amd.conf says not to touch slashes. 140138494Sobrien */ 140238494Sobrienvoid 140338494Sobriendeslashify(char *s) 140438494Sobrien{ 1405174294Sobrien if (!(gopt.flags & CFM_NORMALIZE_SLASHES)) 1406174294Sobrien return; 1407174294Sobrien 140838494Sobrien if (s && *s) { 140938494Sobrien char *sl = s + strlen(s); 141038494Sobrien 141138494Sobrien while (*--sl == '/' && sl > s) 141238494Sobrien *sl = '\0'; 141338494Sobrien } 141438494Sobrien} 141538494Sobrien 141638494Sobrien 141738494Sobrienint 141838494Sobrieneval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map) 141938494Sobrien{ 142038494Sobrien int ok = TRUE; 142138494Sobrien 142238494Sobrien free_opts(fo); 142338494Sobrien 142438494Sobrien /* 142538494Sobrien * Clear out the option table 142638494Sobrien */ 142738494Sobrien memset((voidp) &fs_static, 0, sizeof(fs_static)); 142838494Sobrien memset((voidp) vars, 0, sizeof(vars)); 142938494Sobrien memset((voidp) fo, 0, sizeof(*fo)); 143038494Sobrien 143142629Sobrien /* set hostname */ 143242629Sobrien opt_host = (char *) am_get_hostname(); 143342629Sobrien 143438494Sobrien /* 143538494Sobrien * Set key, map & path before expansion 143638494Sobrien */ 143738494Sobrien opt_key = key; 143838494Sobrien opt_map = map; 143938494Sobrien opt_path = path; 144038494Sobrien 144138494Sobrien opt_dkey = strchr(key, '.'); 144238494Sobrien if (!opt_dkey) { 144338494Sobrien opt_dkey = NullStr; 144438494Sobrien opt_keyd = key; 144538494Sobrien } else { 144638494Sobrien opt_keyd = strnsave(key, opt_dkey - key); 144738494Sobrien opt_dkey++; 144838494Sobrien if (*opt_dkey == '\0') /* check for 'host.' */ 144938494Sobrien opt_dkey = NullStr; 145038494Sobrien } 145138494Sobrien 145238494Sobrien /* 145338494Sobrien * Expand global options 145438494Sobrien */ 1455174294Sobrien fs_static.fs_glob = expand_selectors(g_opts); 145638494Sobrien 145738494Sobrien /* 145838494Sobrien * Expand local options 145938494Sobrien */ 1460174294Sobrien fs_static.fs_local = expand_selectors(opts); 146138494Sobrien 1462174294Sobrien /* break global options into fs_static fields */ 1463174294Sobrien if ((ok = split_opts(fs_static.fs_glob, key))) { 1464174294Sobrien dlog("global split_opts ok"); 1465174294Sobrien /* 1466174294Sobrien * evaluate local selectors 1467174294Sobrien */ 1468174294Sobrien if ((ok = eval_selectors(fs_static.fs_local, key))) { 1469174294Sobrien dlog("local eval_selectors ok"); 1470174294Sobrien /* if the local selectors matched, then do the local overrides */ 1471174294Sobrien ok = split_opts(fs_static.fs_local, key); 1472174294Sobrien if (ok) 1473174294Sobrien dlog("local split_opts ok"); 1474174294Sobrien } 1475174294Sobrien } 147638494Sobrien 147738494Sobrien /* 147842629Sobrien * Normalize remote host name. 147938494Sobrien * 1. Expand variables 148038494Sobrien * 2. Normalize relative to host tables 148138494Sobrien * 3. Strip local domains from the remote host 148238494Sobrien * name before using it in other expansions. 148338494Sobrien * This makes mount point names and other things 148438494Sobrien * much shorter, while allowing cross domain 148538494Sobrien * sharing of mount maps. 148638494Sobrien */ 148738494Sobrien apply_opts(expand_opts, rhost_expansion, FALSE); 148838494Sobrien if (ok && fs_static.opt_rhost && *fs_static.opt_rhost) 148938494Sobrien host_normalize(&fs_static.opt_rhost); 149038494Sobrien 149138494Sobrien /* 149238494Sobrien * Macro expand the options. 149338494Sobrien * Do this regardless of whether we are accepting 149438494Sobrien * this mount - otherwise nasty things happen 149538494Sobrien * with memory allocation. 149638494Sobrien */ 149738494Sobrien apply_opts(expand_opts, expansions, FALSE); 149838494Sobrien 149938494Sobrien /* 150038494Sobrien * Strip trailing slashes from local pathname... 150138494Sobrien */ 150238494Sobrien deslashify(fs_static.opt_fs); 150338494Sobrien 150438494Sobrien /* 150538494Sobrien * ok... copy the data back out. 150638494Sobrien */ 150738494Sobrien *fo = fs_static; 150838494Sobrien 150938494Sobrien /* 151038494Sobrien * Clear defined options 151138494Sobrien */ 151238494Sobrien if (opt_keyd != key && opt_keyd != nullstr) 151338494Sobrien XFREE(opt_keyd); 151438494Sobrien opt_keyd = nullstr; 151538494Sobrien opt_dkey = NullStr; 151638494Sobrien opt_key = opt_map = opt_path = nullstr; 151738494Sobrien 151838494Sobrien return ok; 151938494Sobrien} 1520