Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / am-utils / dist / amd / opts.c
blob6279e2ff73b03056587370d0a93bfce4d6ca833c
1 /* $NetBSD$ */
3 /*
4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1989 Jan-Simon Pendry
6 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1989 The Regents of the University of California.
8 * All rights reserved.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
42 * File: am-utils/amd/opts.c
46 #ifdef HAVE_CONFIG_H
47 # include <config.h>
48 #endif /* HAVE_CONFIG_H */
49 #include <am_defs.h>
50 #include <amd.h>
53 * MACROS:
55 #define NLEN 16 /* Length of longest option name (conservative) */
56 #define S(x) (x) , (sizeof(x)-1)
58 * The BUFSPACE macros checks that there is enough space
59 * left in the expansion buffer. If there isn't then we
60 * give up completely. This is done to avoid crashing the
61 * automounter itself (which would be a bad thing to do).
63 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
66 * TYPEDEFS:
68 typedef int (*IntFuncPtr) (char *);
69 typedef struct opt_apply opt_apply;
70 enum vs_opt { SelEQ, SelNE, VarAss };
73 * STRUCTURES
75 struct opt {
76 char *name; /* Name of the option */
77 int nlen; /* Length of option name */
78 char **optp; /* Pointer to option value string */
79 char **sel_p; /* Pointer to selector value string */
80 int (*fxn_p)(char *); /* Pointer to boolean function */
81 int case_insensitive; /* How to do selector comparisons */
84 struct opt_apply {
85 char **opt;
86 char *val;
89 struct functable {
90 char *name;
91 IntFuncPtr func;
95 * FORWARD DEFINITION:
97 static int f_in_network(char *);
98 static int f_xhost(char *);
99 static int f_netgrp(char *);
100 static int f_netgrpd(char *);
101 static int f_exists(char *);
102 static int f_false(char *);
103 static int f_true(char *);
104 static inline char *expand_options(char *key);
107 * STATICS:
109 static char NullStr[] = "<NULL>";
110 static char nullstr[] = "";
111 static char *opt_dkey = NullStr;
112 static char *opt_host = nullstr; /* XXX: was the global hostname */
113 static char *opt_hostd = hostd;
114 static char *opt_key = nullstr;
115 static char *opt_keyd = nullstr;
116 static char *opt_map = nullstr;
117 static char *opt_path = nullstr;
118 char uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR];
119 char *opt_uid = uid_str;
120 char *opt_gid = gid_str;
121 static char *vars[8];
122 static char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */
125 * GLOBALS
127 static struct am_opts fs_static; /* copy of the options to play with */
131 * Options in some order corresponding to frequency of use so that
132 * first-match algorithm is sped up.
134 static struct opt opt_fields[] = {
135 /* Name and length.
136 Option str. Selector str. boolean fxn. case sensitive */
137 { S("opts"),
138 &fs_static.opt_opts, 0, 0, FALSE },
139 { S("host"),
140 0, &opt_host, 0, TRUE },
141 { S("hostd"),
142 0, &opt_hostd, 0, TRUE },
143 { S("type"),
144 &fs_static.opt_type, 0, 0, FALSE },
145 { S("rhost"),
146 &fs_static.opt_rhost, 0, 0, TRUE },
147 { S("rfs"),
148 &fs_static.opt_rfs, 0, 0, FALSE },
149 { S("fs"),
150 &fs_static.opt_fs, 0, 0, FALSE },
151 { S("key"),
152 0, &opt_key, 0, FALSE },
153 { S("map"),
154 0, &opt_map, 0, FALSE },
155 { S("sublink"),
156 &fs_static.opt_sublink, 0, 0, FALSE },
157 { S("arch"),
158 0, &gopt.arch, 0, TRUE },
159 { S("dev"),
160 &fs_static.opt_dev, 0, 0, FALSE },
161 { S("pref"),
162 &fs_static.opt_pref, 0, 0, FALSE },
163 { S("path"),
164 0, &opt_path, 0, FALSE },
165 { S("autodir"),
166 0, &gopt.auto_dir, 0, FALSE },
167 { S("delay"),
168 &fs_static.opt_delay, 0, 0, FALSE },
169 { S("domain"),
170 0, &hostdomain, 0, TRUE },
171 { S("karch"),
172 0, &gopt.karch, 0, TRUE },
173 { S("cluster"),
174 0, &gopt.cluster, 0, TRUE },
175 { S("wire"),
176 0, 0, f_in_network, TRUE },
177 { S("network"),
178 0, 0, f_in_network, TRUE },
179 { S("netnumber"),
180 0, 0, f_in_network, TRUE },
181 { S("byte"),
182 0, &endian, 0, TRUE },
183 { S("os"),
184 0, &gopt.op_sys, 0, TRUE },
185 { S("osver"),
186 0, &gopt.op_sys_ver, 0, TRUE },
187 { S("full_os"),
188 0, &gopt.op_sys_full, 0, TRUE },
189 { S("vendor"),
190 0, &gopt.op_sys_vendor, 0, TRUE },
191 { S("remopts"),
192 &fs_static.opt_remopts, 0, 0, FALSE },
193 { S("mount"),
194 &fs_static.opt_mount, 0, 0, FALSE },
195 { S("unmount"),
196 &fs_static.opt_unmount, 0, 0, FALSE },
197 { S("umount"),
198 &fs_static.opt_umount, 0, 0, FALSE },
199 { S("cache"),
200 &fs_static.opt_cache, 0, 0, FALSE },
201 { S("user"),
202 &fs_static.opt_user, 0, 0, FALSE },
203 { S("group"),
204 &fs_static.opt_group, 0, 0, FALSE },
205 { S(".key"),
206 0, &opt_dkey, 0, FALSE },
207 { S("key."),
208 0, &opt_keyd, 0, FALSE },
209 { S("maptype"),
210 &fs_static.opt_maptype, 0, 0, FALSE },
211 { S("cachedir"),
212 &fs_static.opt_cachedir, 0, 0, FALSE },
213 { S("addopts"),
214 &fs_static.opt_addopts, 0, 0, FALSE },
215 { S("uid"),
216 0, &opt_uid, 0, FALSE },
217 { S("gid"),
218 0, &opt_gid, 0, FALSE },
219 { S("mount_type"),
220 &fs_static.opt_mount_type, 0, 0, FALSE },
221 { S("dollar"),
222 &literal_dollar, 0, 0, FALSE },
223 { S("var0"),
224 &vars[0], 0, 0, FALSE },
225 { S("var1"),
226 &vars[1], 0, 0, FALSE },
227 { S("var2"),
228 &vars[2], 0, 0, FALSE },
229 { S("var3"),
230 &vars[3], 0, 0, FALSE },
231 { S("var4"),
232 &vars[4], 0, 0, FALSE },
233 { S("var5"),
234 &vars[5], 0, 0, FALSE },
235 { S("var6"),
236 &vars[6], 0, 0, FALSE },
237 { S("var7"),
238 &vars[7], 0, 0, FALSE },
239 { 0, 0, 0, 0, 0, FALSE },
242 static struct functable functable[] = {
243 { "in_network", f_in_network },
244 { "xhost", f_xhost },
245 { "netgrp", f_netgrp },
246 { "netgrpd", f_netgrpd },
247 { "exists", f_exists },
248 { "false", f_false },
249 { "true", f_true },
250 { 0, 0 },
254 * Specially expand the remote host name first
256 static opt_apply rhost_expansion[] =
258 {&fs_static.opt_rhost, "${host}"},
259 {0, 0},
263 * List of options which need to be expanded
264 * Note that the order here _may_ be important.
266 static opt_apply expansions[] =
268 {&fs_static.opt_sublink, 0},
269 {&fs_static.opt_rfs, "${path}"},
270 {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"},
271 {&fs_static.opt_opts, "rw"},
272 {&fs_static.opt_remopts, "${opts}"},
273 {&fs_static.opt_mount, 0},
274 {&fs_static.opt_unmount, 0},
275 {&fs_static.opt_umount, 0},
276 {&fs_static.opt_cachedir, 0},
277 {&fs_static.opt_addopts, 0},
278 {0, 0},
282 * List of options which need to be free'ed before re-use
284 static opt_apply to_free[] =
286 {&fs_static.fs_glob, 0},
287 {&fs_static.fs_local, 0},
288 {&fs_static.fs_mtab, 0},
289 {&fs_static.opt_sublink, 0},
290 {&fs_static.opt_rfs, 0},
291 {&fs_static.opt_fs, 0},
292 {&fs_static.opt_rhost, 0},
293 {&fs_static.opt_opts, 0},
294 {&fs_static.opt_remopts, 0},
295 {&fs_static.opt_mount, 0},
296 {&fs_static.opt_unmount, 0},
297 {&fs_static.opt_umount, 0},
298 {&fs_static.opt_cachedir, 0},
299 {&fs_static.opt_addopts, 0},
300 {&vars[0], 0},
301 {&vars[1], 0},
302 {&vars[2], 0},
303 {&vars[3], 0},
304 {&vars[4], 0},
305 {&vars[5], 0},
306 {&vars[6], 0},
307 {&vars[7], 0},
308 {0, 0},
313 * expand backslash escape sequences
314 * (escaped slash is handled separately in normalize_slash)
316 static char
317 backslash(char **p)
319 char c;
321 if ((*p)[1] == '\0') {
322 plog(XLOG_USER, "Empty backslash escape");
323 return **p;
326 if (**p == '\\') {
327 (*p)++;
328 switch (**p) {
329 case 'g':
330 c = '\007'; /* Bell */
331 break;
332 case 'b':
333 c = '\010'; /* Backspace */
334 break;
335 case 't':
336 c = '\011'; /* Horizontal Tab */
337 break;
338 case 'n':
339 c = '\012'; /* New Line */
340 break;
341 case 'v':
342 c = '\013'; /* Vertical Tab */
343 break;
344 case 'f':
345 c = '\014'; /* Form Feed */
346 break;
347 case 'r':
348 c = '\015'; /* Carriage Return */
349 break;
350 case 'e':
351 c = '\033'; /* Escape */
352 break;
353 case '0':
354 case '1':
355 case '2':
356 case '3':
357 case '4':
358 case '5':
359 case '6':
360 case '7':
362 int cnt, val, ch;
364 for (cnt = 0, val = 0; cnt < 3; cnt++) {
365 ch = *(*p)++;
366 if (ch < '0' || ch > '7') {
367 (*p)--;
368 break;
370 val = (val << 3) | (ch - '0');
373 if ((val & 0xffffff00) != 0)
374 plog(XLOG_USER,
375 "Too large character constant %u\n",
376 val);
377 c = (char) val;
378 --(*p);
380 break;
382 default:
383 c = **p;
384 break;
386 } else
387 c = **p;
389 return c;
394 * Skip to next option in the string
396 static char *
397 opt(char **p)
399 char *cp = *p;
400 char *dp = cp;
401 char *s = cp;
403 top:
404 while (*cp && *cp != ';') {
405 if (*cp == '"') {
407 * Skip past string
409 for (cp++; *cp && *cp != '"'; cp++)
410 if (*cp == '\\')
411 *dp++ = backslash(&cp);
412 else
413 *dp++ = *cp;
414 if (*cp)
415 cp++;
416 } else {
417 *dp++ = *cp++;
422 * Skip past any remaining ';'s
424 while (*cp == ';')
425 cp++;
428 * If we have a zero length string
429 * and there are more fields, then
430 * parse the next one. This allows
431 * sequences of empty fields.
433 if (*cp && dp == s)
434 goto top;
436 *dp = '\0';
438 *p = cp;
439 return s;
444 * These routines add a new style of selector; function-style boolean
445 * operators. To add new ones, just define functions as in true, false,
446 * exists (below) and add them to the functable, above.
448 * Usage example: Some people have X11R5 local, some go to a server. I do
449 * this:
451 * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
452 * -type:=nfs;rfs=/usr/pkg/${key} \
453 * rhost:=server1 \
454 * rhost:=server2
456 * -Rens Troost <rens@imsi.com>
458 static IntFuncPtr
459 functable_lookup(char *key)
461 struct functable *fp;
463 for (fp = functable; fp->name; fp++)
464 if (FSTREQ(fp->name, key))
465 return (fp->func);
466 return (IntFuncPtr) NULL;
471 * Fill in the global structure fs_static by
472 * cracking the string opts. opts may be
473 * scribbled on at will. Does NOT evaluate options.
474 * Returns 0 on error, 1 if no syntax errors were discovered.
476 static int
477 split_opts(char *opts, char *mapkey)
479 char *o = opts;
480 char *f;
483 * For each user-specified option
485 for (f = opt(&o); *f; f = opt(&o)) {
486 struct opt *op;
487 char *eq = strchr(f, '=');
488 char *opt = NULL;
490 if (!eq)
491 continue;
493 if (*(eq-1) == '!' ||
494 eq[1] == '=' ||
495 eq[1] == '!') { /* != or == or =! */
496 continue; /* we don't care about selectors */
499 if (*(eq-1) == ':') { /* := */
500 *(eq-1) = '\0';
501 } else {
502 /* old style assignment */
503 eq[0] = '\0';
505 opt = eq + 1;
508 * For each recognized option
510 for (op = opt_fields; op->name; op++) {
512 * Check whether they match
514 if (FSTREQ(op->name, f)) {
515 if (op->sel_p) {
516 plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
517 mapkey, op->name);
518 return 0;
520 *op->optp = opt; /* actual assignment into fs_static */
521 break; /* break out of for loop */
522 } /* end of "if (FSTREQ(op->name, f))" statement */
523 } /* end of "for (op = opt_fields..." statement */
525 if (!op->name)
526 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
529 return 1;
534 * Just evaluate selectors, which were split by split_opts.
535 * Returns 0 on error or no match, 1 if matched.
537 static int
538 eval_selectors(char *opts, char *mapkey)
540 char *o, *old_o;
541 char *f;
542 int ret = 0;
544 o = old_o = strdup(opts);
547 * For each user-specified option
549 for (f = opt(&o); *f; f = opt(&o)) {
550 struct opt *op;
551 enum vs_opt vs_opt;
552 char *eq = strchr(f, '=');
553 char *fx;
554 IntFuncPtr func;
555 char *opt = NULL;
556 char *arg;
558 if (!eq) {
560 * No value, is it a function call?
562 arg = strchr(f, '(');
564 if (!arg || arg[1] == '\0' || arg == f) {
566 * No, just continue
568 plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
569 continue;
572 /* null-terminate the argument */
573 *arg++ = '\0';
574 fx = strchr(arg, ')');
575 if (!arg || fx == arg) {
576 plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f);
577 continue;
579 *fx = '\0';
581 if (f[0] == '!') {
582 vs_opt = SelNE;
583 f++;
584 } else {
585 vs_opt = SelEQ;
588 * look up f in functable and pass it arg.
589 * func must return 0 on failure, and 1 on success.
591 if ((func = functable_lookup(f))) {
592 int funok;
594 /* this allocates memory, don't forget to free */
595 arg = expand_options(arg);
596 funok = func(arg);
597 XFREE(arg);
599 if (vs_opt == SelNE)
600 funok = !funok;
601 if (!funok)
602 goto out;
604 continue;
605 } else {
606 plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
607 goto out;
609 } else {
610 if (eq[1] == '\0' || eq == f) {
611 /* misformed selector */
612 plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f);
613 continue;
618 * Check what type of operation is happening
619 * !=, =! is SelNE
620 * == is SelEQ
621 * =, := is VarAss
623 if (*(eq-1) == '!') { /* != */
624 vs_opt = SelNE;
625 *(eq-1) = '\0';
626 opt = eq + 1;
627 } else if (*(eq-1) == ':') { /* := */
628 continue;
629 } else if (eq[1] == '=') { /* == */
630 vs_opt = SelEQ;
631 eq[0] = '\0';
632 opt = eq + 2;
633 } else if (eq[1] == '!') { /* =! */
634 vs_opt = SelNE;
635 eq[0] = '\0';
636 opt = eq + 2;
637 } else {
638 /* old style assignment */
639 continue;
643 * For each recognized option
645 for (op = opt_fields; op->name; op++) {
647 * Check whether they match
649 if (FSTREQ(op->name, f)) {
650 opt = expand_options(opt);
652 if (op->sel_p != NULL) {
653 int selok;
654 if (op->case_insensitive) {
655 selok = STRCEQ(*op->sel_p, opt);
656 } else {
657 selok = STREQ(*op->sel_p, opt);
659 if (vs_opt == SelNE)
660 selok = !selok;
661 if (!selok) {
662 plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
663 mapkey,
664 op->name,
665 *op->sel_p,
666 vs_opt == SelNE ? "mis" : "",
667 opt);
668 XFREE(opt);
669 goto out;
671 XFREE(opt);
673 /* check if to apply a function */
674 if (op->fxn_p) {
675 int funok;
677 funok = op->fxn_p(opt);
678 if (vs_opt == SelNE)
679 funok = !funok;
680 if (!funok) {
681 plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
682 mapkey,
683 op->name,
684 vs_opt == SelNE ? "mis" : "",
685 opt);
686 XFREE(opt);
687 goto out;
689 XFREE(opt);
691 break; /* break out of for loop */
695 if (!op->name)
696 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
699 /* all is ok */
700 ret = 1;
702 out:
703 free(old_o);
704 return ret;
709 * Skip to next option in the string, but don't scribble over the string.
710 * However, *p gets repointed to the start of the next string past ';'.
712 static char *
713 opt_no_scribble(char **p)
715 char *cp = *p;
716 char *dp = cp;
717 char *s = cp;
719 top:
720 while (*cp && *cp != ';') {
721 if (*cp == '\"') {
723 * Skip past string
725 cp++;
726 while (*cp && *cp != '\"')
727 *dp++ = *cp++;
728 if (*cp)
729 cp++;
730 } else {
731 *dp++ = *cp++;
736 * Skip past any remaining ';'s
738 while (*cp == ';')
739 cp++;
742 * If we have a zero length string
743 * and there are more fields, then
744 * parse the next one. This allows
745 * sequences of empty fields.
747 if (*cp && dp == s)
748 goto top;
750 *p = cp;
751 return s;
756 * Strip any selectors from a string. Selectors are all assumed to be
757 * first in the string. This is used for the new /defaults method which will
758 * use selectors as well.
760 char *
761 strip_selectors(char *opts, char *mapkey)
764 * Fill in the global structure fs_static by
765 * cracking the string opts. opts may be
766 * scribbled on at will.
768 char *o = opts;
769 char *oo = opts;
770 char *f;
773 * Scan options. Note that the opt() function scribbles on the opt string.
775 while (*(f = opt_no_scribble(&o))) {
776 enum vs_opt vs_opt = VarAss;
777 char *eq = strchr(f, '=');
779 if (!eq || eq[1] == '\0' || eq == f) {
781 * No option or assignment? Return as is.
783 plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f);
784 return o;
787 * Check what type of operation is happening
788 * !=, =! is SelNE
789 * == is SelEQ
790 * := is VarAss
792 if (*(eq-1) == '!') { /* != */
793 vs_opt = SelNE;
794 } else if (*(eq-1) == ':') { /* := */
795 vs_opt = VarAss;
796 } else if (eq[1] == '=') { /* == */
797 vs_opt = SelEQ;
798 } else if (eq[1] == '!') { /* =! */
799 vs_opt = SelNE;
801 switch (vs_opt) {
802 case SelEQ:
803 case SelNE:
804 /* Skip this selector, maybe there's another one following it */
805 plog(XLOG_USER, "skipping selector to \"%s\"", o);
806 /* store previous match. it may have been the first assignment */
807 oo = o;
808 break;
810 case VarAss:
811 /* found the first assignment, return the string starting with it */
812 dlog("found first assignment past selectors \"%s\"", o);
813 return oo;
817 /* return the same string by default. should not happen. */
818 return oo;
822 /*****************************************************************************
823 *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): ***
824 *****************************************************************************/
826 /* test if arg is any of this host's network names or numbers */
827 static int
828 f_in_network(char *arg)
830 int status;
832 if (!arg)
833 return 0;
835 status = is_network_member(arg);
836 dlog("%s is %son a local network", arg, (status ? "" : "not "));
837 return status;
842 * Test if arg is any of this host's names or aliases (CNAMES).
843 * Note: this function compares against the fully expanded host name (hostd).
844 * XXX: maybe we also need to compare against the stripped host name?
846 static int
847 f_xhost(char *arg)
849 struct hostent *hp;
850 char **cp;
852 if (!arg)
853 return 0;
855 /* simple test: does it match main host name? */
856 if (STREQ(arg, opt_hostd))
857 return 1;
859 /* now find all of the names of "arg" and compare against opt_hostd */
860 hp = gethostbyname(arg);
861 if (hp == NULL) {
862 #ifdef HAVE_HSTRERROR
863 plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno));
864 #else /* not HAVE_HSTRERROR */
865 plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno);
866 #endif /* not HAVE_HSTRERROR */
867 return 0;
869 /* check primary name */
870 if (hp->h_name) {
871 dlog("xhost: compare %s==%s", hp->h_name, opt_hostd);
872 if (STREQ(hp->h_name, opt_hostd)) {
873 plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name);
874 return 1;
877 /* check all aliases, if any */
878 if (hp->h_aliases == NULL) {
879 dlog("gethostbyname(%s) has no aliases", arg);
880 return 0;
882 cp = hp->h_aliases;
883 while (*cp) {
884 dlog("xhost: compare alias %s==%s", *cp, opt_hostd);
885 if (STREQ(*cp, opt_hostd)) {
886 plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp);
887 return 1;
889 cp++;
891 /* nothing matched */
892 return 0;
896 /* test if this host (short hostname form) is in netgroup (arg) */
897 static int
898 f_netgrp(char *arg)
900 int status;
901 char *ptr, *nhost;
903 if ((ptr = strchr(arg, ',')) != NULL) {
904 *ptr = '\0';
905 nhost = ptr + 1;
906 } else {
907 nhost = opt_host;
909 status = innetgr(arg, nhost, NULL, NULL);
910 dlog("netgrp = %s status = %d host = %s", arg, status, nhost);
911 if (ptr)
912 *ptr = ',';
913 return status;
917 /* test if this host (fully-qualified name) is in netgroup (arg) */
918 static int
919 f_netgrpd(char *arg)
921 int status;
922 char *ptr, *nhost;
924 if ((ptr = strchr(arg, ',')) != NULL) {
925 *ptr = '\0';
926 nhost = ptr + 1;
927 } else {
928 nhost = opt_hostd;
930 status = innetgr(arg, nhost, NULL, NULL);
931 dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost);
932 if (ptr)
933 *ptr = ',';
934 return status;
938 /* test if file (arg) exists via lstat */
939 static int
940 f_exists(char *arg)
942 struct stat buf;
944 if (lstat(arg, &buf) < 0)
945 return (0);
946 else
947 return (1);
951 /* always false */
952 static int
953 f_false(char *arg)
955 return (0);
959 /* always true */
960 static int
961 f_true(char *arg)
963 return (1);
968 * Free an option
970 static void
971 free_op(opt_apply *p, int b)
973 if (*p->opt) {
974 XFREE(*p->opt);
980 * Normalize slashes in the string.
982 void
983 normalize_slash(char *p)
985 char *f, *f0;
987 if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
988 return;
990 f0 = f = strchr(p, '/');
991 if (f) {
992 char *t = f;
993 do {
994 /* assert(*f == '/'); */
995 if (f == f0 && f[0] == '/' && f[1] == '/') {
996 /* copy double slash iff first */
997 *t++ = *f++;
998 *t++ = *f++;
999 } else {
1000 /* copy a single / across */
1001 *t++ = *f++;
1004 /* assert(f[-1] == '/'); */
1005 /* skip past more /'s */
1006 while (*f == '/')
1007 f++;
1009 /* assert(*f != '/'); */
1010 /* keep copying up to next / */
1011 while (*f && *f != '/') {
1012 /* support escaped slashes '\/' */
1013 if (f[0] == '\\' && f[1] == '/')
1014 f++; /* skip backslash */
1015 *t++ = *f++;
1018 /* assert(*f == 0 || *f == '/'); */
1020 } while (*f);
1021 *t = '\0'; /* derived from fix by Steven Glassman */
1027 * Macro-expand an option. Note that this does not
1028 * handle recursive expansions. They will go badly wrong.
1029 * If sel_p is true then old expand selectors, otherwise
1030 * don't expand selectors.
1032 static char *
1033 expand_op(char *opt, int sel_p)
1035 #define EXPAND_ERROR "No space to expand \"%s\""
1036 char expbuf[MAXPATHLEN + 1];
1037 char nbuf[NLEN + 1];
1038 char *ep = expbuf;
1039 char *cp = opt;
1040 char *dp;
1041 struct opt *op;
1042 char *cp_orig = opt;
1044 while ((dp = strchr(cp, '$'))) {
1045 char ch;
1047 * First copy up to the $
1050 int len = dp - cp;
1052 if (len > 0) {
1053 if (BUFSPACE(ep, len)) {
1055 * We use strncpy (not xstrlcpy) because 'ep' relies on its
1056 * semantics. BUFSPACE guarantees that ep can hold len.
1058 strncpy(ep, cp, len);
1059 ep += len;
1060 } else {
1061 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1062 goto out;
1067 cp = dp + 1;
1068 ch = *cp++;
1069 if (ch == '$') {
1070 if (BUFSPACE(ep, 1)) {
1071 *ep++ = '$';
1072 } else {
1073 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1074 goto out;
1076 } else if (ch == '{') {
1077 /* Expansion... */
1078 enum {
1079 E_All, E_Dir, E_File, E_Domain, E_Host
1080 } todo;
1082 * Find closing brace
1084 char *br_p = strchr(cp, '}');
1085 int len;
1088 * Check we found it
1090 if (!br_p) {
1092 * Just give up
1094 plog(XLOG_USER, "No closing '}' in \"%s\"", opt);
1095 goto out;
1097 len = br_p - cp;
1100 * Figure out which part of the variable to grab.
1102 if (*cp == '/') {
1104 * Just take the last component
1106 todo = E_File;
1107 cp++;
1108 --len;
1109 } else if (*(br_p-1) == '/') {
1111 * Take all but the last component
1113 todo = E_Dir;
1114 --len;
1115 } else if (*cp == '.') {
1117 * Take domain name
1119 todo = E_Domain;
1120 cp++;
1121 --len;
1122 } else if (*(br_p-1) == '.') {
1124 * Take host name
1126 todo = E_Host;
1127 --len;
1128 } else {
1130 * Take the whole lot
1132 todo = E_All;
1136 * Truncate if too long. Since it won't
1137 * match anyway it doesn't matter that
1138 * it has been cut short.
1140 if (len > NLEN)
1141 len = NLEN;
1144 * Put the string into another buffer so
1145 * we can do comparisons.
1147 * We use strncpy here (not xstrlcpy) because the dest is meant
1148 * to be truncated and we don't want to log it as an error. The
1149 * use of the BUFSPACE macro above guarantees the safe use of
1150 * strncpy with nbuf.
1152 strncpy(nbuf, cp, len);
1153 nbuf[len] = '\0';
1156 * Advance cp
1158 cp = br_p + 1;
1161 * Search the option array
1163 for (op = opt_fields; op->name; op++) {
1165 * Check for match
1167 if (len == op->nlen && STREQ(op->name, nbuf)) {
1168 char xbuf[NLEN + 3];
1169 char *val;
1171 * Found expansion. Copy
1172 * the correct value field.
1174 if (!(!op->sel_p == !sel_p)) {
1176 * Copy the string across unexpanded
1178 xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}",
1179 todo == E_File ? "/" :
1180 todo == E_Domain ? "." : "",
1181 nbuf,
1182 todo == E_Dir ? "/" :
1183 todo == E_Host ? "." : "");
1184 val = xbuf;
1186 * Make sure expansion doesn't
1187 * munge the value!
1189 todo = E_All;
1190 } else if (op->sel_p) {
1191 val = *op->sel_p;
1192 } else {
1193 val = *op->optp;
1196 if (val) {
1198 * Do expansion:
1199 * ${/var} means take just the last part
1200 * ${var/} means take all but the last part
1201 * ${.var} means take all but first part
1202 * ${var.} means take just the first part
1203 * ${var} means take the whole lot
1205 int vlen = strlen(val);
1206 char *vptr = val;
1207 switch (todo) {
1208 case E_Dir:
1209 vptr = strrchr(val, '/');
1210 if (vptr)
1211 vlen = vptr - val;
1212 vptr = val;
1213 break;
1214 case E_File:
1215 vptr = strrchr(val, '/');
1216 if (vptr) {
1217 vptr++;
1218 vlen = strlen(vptr);
1219 } else
1220 vptr = val;
1221 break;
1222 case E_Domain:
1223 vptr = strchr(val, '.');
1224 if (vptr) {
1225 vptr++;
1226 vlen = strlen(vptr);
1227 } else {
1228 vptr = "";
1229 vlen = 0;
1231 break;
1232 case E_Host:
1233 vptr = strchr(val, '.');
1234 if (vptr)
1235 vlen = vptr - val;
1236 vptr = val;
1237 break;
1238 case E_All:
1239 break;
1242 if (BUFSPACE(ep, vlen+1)) {
1244 * Don't call xstrlcpy() to truncate a string here. It causes
1245 * spurious xstrlcpy() syslog() errors. Use memcpy() and
1246 * explicitly terminate the string.
1248 memcpy(ep, vptr, vlen+1);
1249 ep += vlen;
1250 *ep = '\0';
1251 } else {
1252 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1253 goto out;
1257 * Done with this variable
1259 break;
1264 * Check that the search was successful
1266 if (!op->name) {
1268 * If it wasn't then scan the
1269 * environment for that name
1270 * and use any value found
1272 char *env = getenv(nbuf);
1274 if (env) {
1275 int vlen = strlen(env);
1277 if (BUFSPACE(ep, vlen+1)) {
1278 xstrlcpy(ep, env, vlen+1);
1279 ep += vlen;
1280 } else {
1281 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1282 goto out;
1284 if (amuDebug(D_STR))
1285 plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
1286 } else {
1287 plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
1290 } else {
1292 * Error, error
1294 plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt);
1298 out:
1300 * Handle common case - no expansion
1302 if (cp == opt) {
1303 opt = strdup(cp);
1304 } else {
1306 * Finish off the expansion
1308 int vlen = strlen(cp);
1309 if (BUFSPACE(ep, vlen+1)) {
1310 xstrlcpy(ep, cp, vlen+1);
1311 /* ep += vlen; */
1312 } else {
1313 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1317 * Save the expansion
1319 opt = strdup(expbuf);
1322 normalize_slash(opt);
1324 if (amuDebug(D_STR)) {
1325 plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
1326 plog(XLOG_DEBUG, "......... is \"%s\"", opt);
1328 return opt;
1333 * Wrapper for expand_op
1335 static void
1336 expand_opts(opt_apply *p, int sel_p)
1338 if (*p->opt) {
1339 *p->opt = expand_op(*p->opt, sel_p);
1340 } else if (p->val) {
1342 * Do double expansion, remembering
1343 * to free the string from the first
1344 * expansion...
1346 char *s = expand_op(p->val, TRUE);
1347 *p->opt = expand_op(s, sel_p);
1348 XFREE(s);
1354 * Apply a function to a list of options
1356 static void
1357 apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b)
1359 opt_apply *pp;
1361 for (pp = ppp; pp->opt; pp++)
1362 (*op) (pp, b);
1367 * Free the option table
1369 void
1370 free_opts(am_opts *fo)
1373 * Copy in the structure we are playing with
1375 fs_static = *fo;
1378 * Free previously allocated memory
1380 apply_opts(free_op, to_free, FALSE);
1385 * Expand selectors (variables that cannot be assigned to or overridden)
1387 char *
1388 expand_selectors(char *key)
1390 return expand_op(key, TRUE);
1395 * Expand options (i.e. non-selectors, see above for definition)
1397 static inline char *
1398 expand_options(char *key)
1400 return expand_op(key, FALSE);
1405 * Remove trailing /'s from a string
1406 * unless the string is a single / (Steven Glassman)
1407 * or unless it is two slashes // (Kevin D. Bond)
1408 * or unless amd.conf says not to touch slashes.
1410 void
1411 deslashify(char *s)
1413 if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
1414 return;
1416 if (s && *s) {
1417 char *sl = s + strlen(s);
1419 while (*--sl == '/' && sl > s)
1420 *sl = '\0';
1426 eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map)
1428 int ok = TRUE;
1430 free_opts(fo);
1433 * Clear out the option table
1435 memset((voidp) &fs_static, 0, sizeof(fs_static));
1436 memset((voidp) vars, 0, sizeof(vars));
1437 memset((voidp) fo, 0, sizeof(*fo));
1439 /* set hostname */
1440 opt_host = (char *) am_get_hostname();
1443 * Set key, map & path before expansion
1445 opt_key = key;
1446 opt_map = map;
1447 opt_path = path;
1449 opt_dkey = strchr(key, '.');
1450 if (!opt_dkey) {
1451 opt_dkey = NullStr;
1452 opt_keyd = key;
1453 } else {
1454 opt_keyd = strnsave(key, opt_dkey - key);
1455 opt_dkey++;
1456 if (*opt_dkey == '\0') /* check for 'host.' */
1457 opt_dkey = NullStr;
1461 * Expand global options
1463 fs_static.fs_glob = expand_selectors(g_opts);
1466 * Expand local options
1468 fs_static.fs_local = expand_selectors(opts);
1470 /* break global options into fs_static fields */
1471 if ((ok = split_opts(fs_static.fs_glob, key))) {
1472 dlog("global split_opts ok");
1474 * evaluate local selectors
1476 if ((ok = eval_selectors(fs_static.fs_local, key))) {
1477 dlog("local eval_selectors ok");
1478 /* if the local selectors matched, then do the local overrides */
1479 ok = split_opts(fs_static.fs_local, key);
1480 if (ok)
1481 dlog("local split_opts ok");
1486 * Normalize remote host name.
1487 * 1. Expand variables
1488 * 2. Normalize relative to host tables
1489 * 3. Strip local domains from the remote host
1490 * name before using it in other expansions.
1491 * This makes mount point names and other things
1492 * much shorter, while allowing cross domain
1493 * sharing of mount maps.
1495 apply_opts(expand_opts, rhost_expansion, FALSE);
1496 if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
1497 host_normalize(&fs_static.opt_rhost);
1500 * Macro expand the options.
1501 * Do this regardless of whether we are accepting
1502 * this mount - otherwise nasty things happen
1503 * with memory allocation.
1505 apply_opts(expand_opts, expansions, FALSE);
1508 * Strip trailing slashes from local pathname...
1510 deslashify(fs_static.opt_fs);
1513 * ok... copy the data back out.
1515 *fo = fs_static;
1518 * Clear defined options
1520 if (opt_keyd != key && opt_keyd != nullstr)
1521 XFREE(opt_keyd);
1522 opt_keyd = nullstr;
1523 opt_dkey = NullStr;
1524 opt_key = opt_map = opt_path = nullstr;
1526 return ok;