1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2014 The FreeBSD Foundation
5 *
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/ioctl.h>
35 #include <sys/param.h>
36 #include <sys/linker.h>
37 #include <sys/mount.h>
38 #include <sys/socket.h>
39 #include <sys/stat.h>
40 #include <sys/wait.h>
41 #include <sys/utsname.h>
42 #include <assert.h>
43 #include <ctype.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <libgen.h>
48 #include <libutil.h>
49 #include <netdb.h>
50 #include <paths.h>
51 #include <signal.h>
52 #include <stdbool.h>
53 #include <stdint.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58
59 #include "autofs_ioctl.h"
60
61 #include "common.h"
62
63 extern FILE *yyin;
64 extern char *yytext;
65 extern int yylex(void);
66
67 static void parse_master_yyin(struct node *root, const char *master);
68 static void parse_map_yyin(struct node *parent, const char *map,
69 const char *executable_key);
70
71 char *
checked_strdup(const char * s)72 checked_strdup(const char *s)
73 {
74 char *c;
75
76 assert(s != NULL);
77
78 c = strdup(s);
79 if (c == NULL)
80 log_err(1, "strdup");
81 return (c);
82 }
83
84 /*
85 * Concatenate two strings, inserting separator between them, unless not needed.
86 */
87 char *
concat(const char * s1,char separator,const char * s2)88 concat(const char *s1, char separator, const char *s2)
89 {
90 char *result;
91 char s1last, s2first;
92 int ret;
93
94 if (s1 == NULL)
95 s1 = "";
96 if (s2 == NULL)
97 s2 = "";
98
99 if (s1[0] == '\0')
100 s1last = '\0';
101 else
102 s1last = s1[strlen(s1) - 1];
103
104 s2first = s2[0];
105
106 if (s1last == separator && s2first == separator) {
107 /*
108 * If s1 ends with the separator and s2 begins with
109 * it - skip the latter; otherwise concatenating "/"
110 * and "/foo" would end up returning "//foo".
111 */
112 ret = asprintf(&result, "%s%s", s1, s2 + 1);
113 } else if (s1last == separator || s2first == separator ||
114 s1[0] == '\0' || s2[0] == '\0') {
115 ret = asprintf(&result, "%s%s", s1, s2);
116 } else {
117 ret = asprintf(&result, "%s%c%s", s1, separator, s2);
118 }
119 if (ret < 0)
120 log_err(1, "asprintf");
121
122 //log_debugx("%s: got %s and %s, returning %s", __func__, s1, s2, result);
123
124 return (result);
125 }
126
127 void
create_directory(const char * path)128 create_directory(const char *path)
129 {
130 char *component, *copy, *tofree, *partial, *tmp;
131 int error;
132
133 assert(path[0] == '/');
134
135 /*
136 * +1 to skip the leading slash.
137 */
138 copy = tofree = checked_strdup(path + 1);
139
140 partial = checked_strdup("/");
141 for (;;) {
142 component = strsep(©, "/");
143 if (component == NULL)
144 break;
145 tmp = concat(partial, '/', component);
146 free(partial);
147 partial = tmp;
148 //log_debugx("creating \"%s\"", partial);
149 error = mkdir(partial, 0755);
150 if (error != 0 && errno != EEXIST) {
151 log_warn("cannot create %s", partial);
152 break;
153 }
154 }
155
156 free(partial);
157 free(tofree);
158 }
159
160 struct node *
node_new_root(void)161 node_new_root(void)
162 {
163 struct node *n;
164
165 n = calloc(1, sizeof(*n));
166 if (n == NULL)
167 log_err(1, "calloc");
168 // XXX
169 n->n_key = checked_strdup("/");
170 n->n_options = checked_strdup("");
171
172 TAILQ_INIT(&n->n_children);
173
174 return (n);
175 }
176
177 struct node *
node_new(struct node * parent,char * key,char * options,char * location,const char * config_file,int config_line)178 node_new(struct node *parent, char *key, char *options, char *location,
179 const char *config_file, int config_line)
180 {
181 struct node *n;
182
183 n = calloc(1, sizeof(*n));
184 if (n == NULL)
185 log_err(1, "calloc");
186
187 TAILQ_INIT(&n->n_children);
188 assert(key != NULL);
189 assert(key[0] != '\0');
190 n->n_key = key;
191 if (options != NULL)
192 n->n_options = options;
193 else
194 n->n_options = strdup("");
195 n->n_location = location;
196 assert(config_file != NULL);
197 n->n_config_file = config_file;
198 assert(config_line >= 0);
199 n->n_config_line = config_line;
200
201 assert(parent != NULL);
202 n->n_parent = parent;
203 TAILQ_INSERT_TAIL(&parent->n_children, n, n_next);
204
205 return (n);
206 }
207
208 struct node *
node_new_map(struct node * parent,char * key,char * options,char * map,const char * config_file,int config_line)209 node_new_map(struct node *parent, char *key, char *options, char *map,
210 const char *config_file, int config_line)
211 {
212 struct node *n;
213
214 n = calloc(1, sizeof(*n));
215 if (n == NULL)
216 log_err(1, "calloc");
217
218 TAILQ_INIT(&n->n_children);
219 assert(key != NULL);
220 assert(key[0] != '\0');
221 n->n_key = key;
222 if (options != NULL)
223 n->n_options = options;
224 else
225 n->n_options = strdup("");
226 n->n_map = map;
227 assert(config_file != NULL);
228 n->n_config_file = config_file;
229 assert(config_line >= 0);
230 n->n_config_line = config_line;
231
232 assert(parent != NULL);
233 n->n_parent = parent;
234 TAILQ_INSERT_TAIL(&parent->n_children, n, n_next);
235
236 return (n);
237 }
238
239 static struct node *
node_duplicate(const struct node * o,struct node * parent)240 node_duplicate(const struct node *o, struct node *parent)
241 {
242 const struct node *child;
243 struct node *n;
244
245 if (parent == NULL)
246 parent = o->n_parent;
247
248 n = node_new(parent, o->n_key, o->n_options, o->n_location,
249 o->n_config_file, o->n_config_line);
250
251 TAILQ_FOREACH(child, &o->n_children, n_next)
252 node_duplicate(child, n);
253
254 return (n);
255 }
256
257 static void
node_delete(struct node * n)258 node_delete(struct node *n)
259 {
260 struct node *child, *tmp;
261
262 assert (n != NULL);
263
264 TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp)
265 node_delete(child);
266
267 if (n->n_parent != NULL)
268 TAILQ_REMOVE(&n->n_parent->n_children, n, n_next);
269
270 free(n);
271 }
272
273 /*
274 * Move (reparent) node 'n' to make it sibling of 'previous', placed
275 * just after it.
276 */
277 static void
node_move_after(struct node * n,struct node * previous)278 node_move_after(struct node *n, struct node *previous)
279 {
280
281 TAILQ_REMOVE(&n->n_parent->n_children, n, n_next);
282 n->n_parent = previous->n_parent;
283 TAILQ_INSERT_AFTER(&previous->n_parent->n_children, previous, n, n_next);
284 }
285
286 static void
node_expand_includes(struct node * root,bool is_master)287 node_expand_includes(struct node *root, bool is_master)
288 {
289 struct node *n, *n2, *tmp, *tmp2, *tmproot;
290 int error;
291
292 TAILQ_FOREACH_SAFE(n, &root->n_children, n_next, tmp) {
293 if (n->n_key[0] != '+')
294 continue;
295
296 error = access(AUTO_INCLUDE_PATH, F_OK);
297 if (error != 0) {
298 log_errx(1, "directory services not configured; "
299 "%s does not exist", AUTO_INCLUDE_PATH);
300 }
301
302 /*
303 * "+1" to skip leading "+".
304 */
305 yyin = auto_popen(AUTO_INCLUDE_PATH, n->n_key + 1, NULL);
306 assert(yyin != NULL);
307
308 tmproot = node_new_root();
309 if (is_master)
310 parse_master_yyin(tmproot, n->n_key);
311 else
312 parse_map_yyin(tmproot, n->n_key, NULL);
313
314 error = auto_pclose(yyin);
315 yyin = NULL;
316 if (error != 0) {
317 log_errx(1, "failed to handle include \"%s\"",
318 n->n_key);
319 }
320
321 /*
322 * Entries to be included are now in tmproot. We need to merge
323 * them with the rest, preserving their place and ordering.
324 */
325 TAILQ_FOREACH_REVERSE_SAFE(n2,
326 &tmproot->n_children, nodehead, n_next, tmp2) {
327 node_move_after(n2, n);
328 }
329
330 node_delete(n);
331 node_delete(tmproot);
332 }
333 }
334
335 static char *
expand_ampersand(char * string,const char * key)336 expand_ampersand(char *string, const char *key)
337 {
338 char c, *expanded;
339 int i, ret, before_len = 0;
340 bool backslashed = false;
341
342 assert(key[0] != '\0');
343
344 expanded = checked_strdup(string);
345
346 for (i = 0; string[i] != '\0'; i++) {
347 c = string[i];
348 if (c == '\\' && backslashed == false) {
349 backslashed = true;
350 continue;
351 }
352 if (backslashed) {
353 backslashed = false;
354 continue;
355 }
356 backslashed = false;
357 if (c != '&')
358 continue;
359
360 /*
361 * The 'before_len' variable contains the number
362 * of characters before the '&'.
363 */
364 before_len = i;
365 //assert(i < (int)strlen(string));
366
367 ret = asprintf(&expanded, "%.*s%s%s",
368 before_len, string, key, string + before_len + 1);
369 if (ret < 0)
370 log_err(1, "asprintf");
371
372 //log_debugx("\"%s\" expanded with key \"%s\" to \"%s\"",
373 // string, key, expanded);
374
375 /*
376 * Figure out where to start searching for next variable.
377 */
378 string = expanded;
379 i = before_len + strlen(key);
380 if (i == (int)strlen(string))
381 break;
382 backslashed = false;
383 //assert(i < (int)strlen(string));
384 }
385
386 return (expanded);
387 }
388
389 /*
390 * Expand "&" in n_location. If the key is NULL, try to use
391 * key from map entries themselves. Keep in mind that maps
392 * consist of tho levels of node structures, the key is one
393 * level up.
394 *
395 * Variant with NULL key is for "automount -LL".
396 */
397 void
node_expand_ampersand(struct node * n,const char * key)398 node_expand_ampersand(struct node *n, const char *key)
399 {
400 struct node *child;
401
402 if (n->n_location != NULL) {
403 if (key == NULL) {
404 if (n->n_parent != NULL &&
405 strcmp(n->n_parent->n_key, "*") != 0) {
406 n->n_location = expand_ampersand(n->n_location,
407 n->n_parent->n_key);
408 }
409 } else {
410 n->n_location = expand_ampersand(n->n_location, key);
411 }
412 }
413
414 TAILQ_FOREACH(child, &n->n_children, n_next)
415 node_expand_ampersand(child, key);
416 }
417
418 /*
419 * Expand "*" in n_key.
420 */
421 void
node_expand_wildcard(struct node * n,const char * key)422 node_expand_wildcard(struct node *n, const char *key)
423 {
424 struct node *child, *expanded;
425
426 assert(key != NULL);
427
428 if (strcmp(n->n_key, "*") == 0) {
429 expanded = node_duplicate(n, NULL);
430 expanded->n_key = checked_strdup(key);
431 node_move_after(expanded, n);
432 }
433
434 TAILQ_FOREACH(child, &n->n_children, n_next)
435 node_expand_wildcard(child, key);
436 }
437
438 int
node_expand_defined(struct node * n)439 node_expand_defined(struct node *n)
440 {
441 struct node *child;
442 int error, cumulated_error = 0;
443
444 if (n->n_location != NULL) {
445 n->n_location = defined_expand(n->n_location);
446 if (n->n_location == NULL) {
447 log_warnx("failed to expand location for %s",
448 node_path(n));
449 return (EINVAL);
450 }
451 }
452
453 TAILQ_FOREACH(child, &n->n_children, n_next) {
454 error = node_expand_defined(child);
455 if (error != 0 && cumulated_error == 0)
456 cumulated_error = error;
457 }
458
459 return (cumulated_error);
460 }
461
462 static bool
node_is_direct_key(const struct node * n)463 node_is_direct_key(const struct node *n)
464 {
465
466 if (n->n_parent != NULL && n->n_parent->n_parent == NULL &&
467 strcmp(n->n_key, "/-") == 0) {
468 return (true);
469 }
470
471 return (false);
472 }
473
474 bool
node_is_direct_map(const struct node * n)475 node_is_direct_map(const struct node *n)
476 {
477
478 for (;;) {
479 assert(n->n_parent != NULL);
480 if (n->n_parent->n_parent == NULL)
481 break;
482 n = n->n_parent;
483 }
484
485 return (node_is_direct_key(n));
486 }
487
488 bool
node_has_wildcards(const struct node * n)489 node_has_wildcards(const struct node *n)
490 {
491 const struct node *child;
492
493 TAILQ_FOREACH(child, &n->n_children, n_next) {
494 if (strcmp(child->n_key, "*") == 0)
495 return (true);
496 }
497
498 return (false);
499 }
500
501 static void
node_expand_maps(struct node * n,bool indirect)502 node_expand_maps(struct node *n, bool indirect)
503 {
504 struct node *child, *tmp;
505
506 TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) {
507 if (node_is_direct_map(child)) {
508 if (indirect)
509 continue;
510 } else {
511 if (indirect == false)
512 continue;
513 }
514
515 /*
516 * This is the first-level map node; the one that contains
517 * the key and subnodes with mountpoints and actual map names.
518 */
519 if (child->n_map == NULL)
520 continue;
521
522 if (indirect) {
523 log_debugx("map \"%s\" is an indirect map, parsing",
524 child->n_map);
525 } else {
526 log_debugx("map \"%s\" is a direct map, parsing",
527 child->n_map);
528 }
529 parse_map(child, child->n_map, NULL, NULL);
530 }
531 }
532
533 static void
node_expand_direct_maps(struct node * n)534 node_expand_direct_maps(struct node *n)
535 {
536
537 node_expand_maps(n, false);
538 }
539
540 void
node_expand_indirect_maps(struct node * n)541 node_expand_indirect_maps(struct node *n)
542 {
543
544 node_expand_maps(n, true);
545 }
546
547 static char *
node_path_x(const struct node * n,char * x)548 node_path_x(const struct node *n, char *x)
549 {
550 char *path;
551
552 if (n->n_parent == NULL)
553 return (x);
554
555 /*
556 * Return "/-" for direct maps only if we were asked for path
557 * to the "/-" node itself, not to any of its subnodes.
558 */
559 if (node_is_direct_key(n) && x[0] != '\0')
560 return (x);
561
562 assert(n->n_key[0] != '\0');
563 path = concat(n->n_key, '/', x);
564 free(x);
565
566 return (node_path_x(n->n_parent, path));
567 }
568
569 /*
570 * Return full path for node, consisting of concatenated
571 * paths of node itself and all its parents, up to the root.
572 */
573 char *
node_path(const struct node * n)574 node_path(const struct node *n)
575 {
576 char *path;
577 size_t len;
578
579 path = node_path_x(n, checked_strdup(""));
580
581 /*
582 * Strip trailing slash, unless the whole path is "/".
583 */
584 len = strlen(path);
585 if (len > 1 && path[len - 1] == '/')
586 path[len - 1] = '\0';
587
588 return (path);
589 }
590
591 static char *
node_options_x(const struct node * n,char * x)592 node_options_x(const struct node *n, char *x)
593 {
594 char *options;
595
596 if (n == NULL)
597 return (x);
598
599 options = concat(x, ',', n->n_options);
600 free(x);
601
602 return (node_options_x(n->n_parent, options));
603 }
604
605 /*
606 * Return options for node, consisting of concatenated
607 * options from the node itself and all its parents,
608 * up to the root.
609 */
610 char *
node_options(const struct node * n)611 node_options(const struct node *n)
612 {
613
614 return (node_options_x(n, checked_strdup("")));
615 }
616
617 static void
node_print_indent(const struct node * n,const char * cmdline_options,int indent)618 node_print_indent(const struct node *n, const char *cmdline_options,
619 int indent)
620 {
621 const struct node *child, *first_child;
622 char *path, *options, *tmp;
623
624 path = node_path(n);
625 tmp = node_options(n);
626 options = concat(cmdline_options, ',', tmp);
627 free(tmp);
628
629 /*
630 * Do not show both parent and child node if they have the same
631 * mountpoint; only show the child node. This means the typical,
632 * "key location", map entries are shown in a single line;
633 * the "key mountpoint1 location2 mountpoint2 location2" entries
634 * take multiple lines.
635 */
636 first_child = TAILQ_FIRST(&n->n_children);
637 if (first_child == NULL || TAILQ_NEXT(first_child, n_next) != NULL ||
638 strcmp(path, node_path(first_child)) != 0) {
639 assert(n->n_location == NULL || n->n_map == NULL);
640 printf("%*.s%-*s %s%-*s %-*s # %s map %s at %s:%d\n",
641 indent, "",
642 25 - indent,
643 path,
644 options[0] != '\0' ? "-" : " ",
645 20,
646 options[0] != '\0' ? options : "",
647 20,
648 n->n_location != NULL ? n->n_location : n->n_map != NULL ? n->n_map : "",
649 node_is_direct_map(n) ? "direct" : "indirect",
650 indent == 0 ? "referenced" : "defined",
651 n->n_config_file, n->n_config_line);
652 }
653
654 free(path);
655 free(options);
656
657 TAILQ_FOREACH(child, &n->n_children, n_next)
658 node_print_indent(child, cmdline_options, indent + 2);
659 }
660
661 /*
662 * Recursively print node with all its children. The cmdline_options
663 * argument is used for additional options to be prepended to all the
664 * others - usually those are the options passed by command line.
665 */
666 void
node_print(const struct node * n,const char * cmdline_options)667 node_print(const struct node *n, const char *cmdline_options)
668 {
669 const struct node *child;
670
671 TAILQ_FOREACH(child, &n->n_children, n_next)
672 node_print_indent(child, cmdline_options, 0);
673 }
674
675 static struct node *
node_find_x(struct node * node,const char * path)676 node_find_x(struct node *node, const char *path)
677 {
678 struct node *child, *found;
679 char *tmp;
680 size_t tmplen;
681
682 //log_debugx("looking up %s in %s", path, node_path(node));
683
684 if (!node_is_direct_key(node)) {
685 tmp = node_path(node);
686 tmplen = strlen(tmp);
687 if (strncmp(tmp, path, tmplen) != 0) {
688 free(tmp);
689 return (NULL);
690 }
691 if (path[tmplen] != '/' && path[tmplen] != '\0') {
692 /*
693 * If we have two map entries like 'foo' and 'foobar', make
694 * sure the search for 'foobar' won't match 'foo' instead.
695 */
696 free(tmp);
697 return (NULL);
698 }
699 free(tmp);
700 }
701
702 TAILQ_FOREACH(child, &node->n_children, n_next) {
703 found = node_find_x(child, path);
704 if (found != NULL)
705 return (found);
706 }
707
708 if (node->n_parent == NULL || node_is_direct_key(node))
709 return (NULL);
710
711 return (node);
712 }
713
714 struct node *
node_find(struct node * root,const char * path)715 node_find(struct node *root, const char *path)
716 {
717 struct node *node;
718
719 assert(root->n_parent == NULL);
720
721 node = node_find_x(root, path);
722 if (node != NULL)
723 assert(node != root);
724
725 return (node);
726 }
727
728 /*
729 * Canonical form of a map entry looks like this:
730 *
731 * key [-options] [ [/mountpoint] [-options2] location ... ]
732 *
733 * Entries for executable maps are slightly different, as they
734 * lack the 'key' field and are always single-line; the key field
735 * for those maps is taken from 'executable_key' argument.
736 *
737 * We parse it in such a way that a map always has two levels - first
738 * for key, and the second, for the mountpoint.
739 */
740 static void
parse_map_yyin(struct node * parent,const char * map,const char * executable_key)741 parse_map_yyin(struct node *parent, const char *map, const char *executable_key)
742 {
743 char *key = NULL, *options = NULL, *mountpoint = NULL,
744 *options2 = NULL, *location = NULL;
745 int ret;
746 struct node *node;
747
748 lineno = 1;
749
750 if (executable_key != NULL)
751 key = checked_strdup(executable_key);
752
753 for (;;) {
754 ret = yylex();
755 if (ret == 0 || ret == NEWLINE) {
756 /*
757 * In case of executable map, the key is always
758 * non-NULL, even if the map is empty. So, make sure
759 * we don't fail empty maps here.
760 */
761 if ((key != NULL && executable_key == NULL) ||
762 options != NULL) {
763 log_errx(1, "truncated entry at %s, line %d",
764 map, lineno);
765 }
766 if (ret == 0 || executable_key != NULL) {
767 /*
768 * End of file.
769 */
770 break;
771 } else {
772 key = options = NULL;
773 continue;
774 }
775 }
776 if (key == NULL) {
777 key = checked_strdup(yytext);
778 if (key[0] == '+') {
779 node_new(parent, key, NULL, NULL, map, lineno);
780 key = options = NULL;
781 continue;
782 }
783 continue;
784 } else if (yytext[0] == '-') {
785 if (options != NULL) {
786 log_errx(1, "duplicated options at %s, line %d",
787 map, lineno);
788 }
789 /*
790 * +1 to skip leading "-".
791 */
792 options = checked_strdup(yytext + 1);
793 continue;
794 }
795
796 /*
797 * We cannot properly handle a situation where the map key
798 * is "/". Ignore such entries.
799 *
800 * XXX: According to Piete Brooks, Linux automounter uses
801 * "/" as a wildcard character in LDAP maps. Perhaps
802 * we should work around this braindamage by substituting
803 * "*" for "/"?
804 */
805 if (strcmp(key, "/") == 0) {
806 log_warnx("nonsensical map key \"/\" at %s, line %d; "
807 "ignoring map entry ", map, lineno);
808
809 /*
810 * Skip the rest of the entry.
811 */
812 do {
813 ret = yylex();
814 } while (ret != 0 && ret != NEWLINE);
815
816 key = options = NULL;
817 continue;
818 }
819
820 //log_debugx("adding map node, %s", key);
821 node = node_new(parent, key, options, NULL, map, lineno);
822 key = options = NULL;
823
824 for (;;) {
825 if (yytext[0] == '/') {
826 if (mountpoint != NULL) {
827 log_errx(1, "duplicated mountpoint "
828 "in %s, line %d", map, lineno);
829 }
830 if (options2 != NULL || location != NULL) {
831 log_errx(1, "mountpoint out of order "
832 "in %s, line %d", map, lineno);
833 }
834 mountpoint = checked_strdup(yytext);
835 goto again;
836 }
837
838 if (yytext[0] == '-') {
839 if (options2 != NULL) {
840 log_errx(1, "duplicated options "
841 "in %s, line %d", map, lineno);
842 }
843 if (location != NULL) {
844 log_errx(1, "options out of order "
845 "in %s, line %d", map, lineno);
846 }
847 options2 = checked_strdup(yytext + 1);
848 goto again;
849 }
850
851 if (location != NULL) {
852 log_errx(1, "too many arguments "
853 "in %s, line %d", map, lineno);
854 }
855
856 /*
857 * If location field starts with colon, e.g. ":/dev/cd0",
858 * then strip it.
859 */
860 if (yytext[0] == ':') {
861 location = checked_strdup(yytext + 1);
862 if (location[0] == '\0') {
863 log_errx(1, "empty location in %s, "
864 "line %d", map, lineno);
865 }
866 } else {
867 location = checked_strdup(yytext);
868 }
869
870 if (mountpoint == NULL)
871 mountpoint = checked_strdup("/");
872 if (options2 == NULL)
873 options2 = checked_strdup("");
874
875 #if 0
876 log_debugx("adding map node, %s %s %s",
877 mountpoint, options2, location);
878 #endif
879 node_new(node, mountpoint, options2, location,
880 map, lineno);
881 mountpoint = options2 = location = NULL;
882 again:
883 ret = yylex();
884 if (ret == 0 || ret == NEWLINE) {
885 if (mountpoint != NULL || options2 != NULL ||
886 location != NULL) {
887 log_errx(1, "truncated entry "
888 "in %s, line %d", map, lineno);
889 }
890 break;
891 }
892 }
893 }
894 }
895
896 /*
897 * Parse output of a special map called without argument. It is a list
898 * of keys, separated by newlines. They can contain whitespace, so use
899 * getline(3) instead of lexer used for maps.
900 */
901 static void
parse_map_keys_yyin(struct node * parent,const char * map)902 parse_map_keys_yyin(struct node *parent, const char *map)
903 {
904 char *line = NULL, *key;
905 size_t linecap = 0;
906 ssize_t linelen;
907
908 lineno = 1;
909
910 for (;;) {
911 linelen = getline(&line, &linecap, yyin);
912 if (linelen < 0) {
913 /*
914 * End of file.
915 */
916 break;
917 }
918 if (linelen <= 1) {
919 /*
920 * Empty line, consisting of just the newline.
921 */
922 continue;
923 }
924
925 /*
926 * "-1" to strip the trailing newline.
927 */
928 key = strndup(line, linelen - 1);
929
930 log_debugx("adding key \"%s\"", key);
931 node_new(parent, key, NULL, NULL, map, lineno);
932 lineno++;
933 }
934 free(line);
935 }
936
937 static bool
file_is_executable(const char * path)938 file_is_executable(const char *path)
939 {
940 struct stat sb;
941 int error;
942
943 error = stat(path, &sb);
944 if (error != 0)
945 log_err(1, "cannot stat %s", path);
946 if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) ||
947 (sb.st_mode & S_IXOTH))
948 return (true);
949 return (false);
950 }
951
952 /*
953 * Parse a special map, e.g. "-hosts".
954 */
955 static void
parse_special_map(struct node * parent,const char * map,const char * key)956 parse_special_map(struct node *parent, const char *map, const char *key)
957 {
958 char *path;
959 int error, ret;
960
961 assert(map[0] == '-');
962
963 /*
964 * +1 to skip leading "-" in map name.
965 */
966 ret = asprintf(&path, "%s/special_%s", AUTO_SPECIAL_PREFIX, map + 1);
967 if (ret < 0)
968 log_err(1, "asprintf");
969
970 yyin = auto_popen(path, key, NULL);
971 assert(yyin != NULL);
972
973 if (key == NULL) {
974 parse_map_keys_yyin(parent, map);
975 } else {
976 parse_map_yyin(parent, map, key);
977 }
978
979 error = auto_pclose(yyin);
980 yyin = NULL;
981 if (error != 0)
982 log_errx(1, "failed to handle special map \"%s\"", map);
983
984 node_expand_includes(parent, false);
985 node_expand_direct_maps(parent);
986
987 free(path);
988 }
989
990 /*
991 * Retrieve and parse map from directory services, e.g. LDAP.
992 * Note that it is different from executable maps, in that
993 * the include script outputs the whole map to standard output
994 * (as opposed to executable maps that only output a single
995 * entry, without the key), and it takes the map name as an
996 * argument, instead of key.
997 */
998 static void
parse_included_map(struct node * parent,const char * map)999 parse_included_map(struct node *parent, const char *map)
1000 {
1001 int error;
1002
1003 assert(map[0] != '-');
1004 assert(map[0] != '/');
1005
1006 error = access(AUTO_INCLUDE_PATH, F_OK);
1007 if (error != 0) {
1008 log_errx(1, "directory services not configured;"
1009 " %s does not exist", AUTO_INCLUDE_PATH);
1010 }
1011
1012 yyin = auto_popen(AUTO_INCLUDE_PATH, map, NULL);
1013 assert(yyin != NULL);
1014
1015 parse_map_yyin(parent, map, NULL);
1016
1017 error = auto_pclose(yyin);
1018 yyin = NULL;
1019 if (error != 0)
1020 log_errx(1, "failed to handle remote map \"%s\"", map);
1021
1022 node_expand_includes(parent, false);
1023 node_expand_direct_maps(parent);
1024 }
1025
1026 void
parse_map(struct node * parent,const char * map,const char * key,bool * wildcards)1027 parse_map(struct node *parent, const char *map, const char *key,
1028 bool *wildcards)
1029 {
1030 char *path = NULL;
1031 int error, ret;
1032 bool executable;
1033
1034 assert(map != NULL);
1035 assert(map[0] != '\0');
1036
1037 log_debugx("parsing map \"%s\"", map);
1038
1039 if (wildcards != NULL)
1040 *wildcards = false;
1041
1042 if (map[0] == '-') {
1043 if (wildcards != NULL)
1044 *wildcards = true;
1045 return (parse_special_map(parent, map, key));
1046 }
1047
1048 if (map[0] == '/') {
1049 path = checked_strdup(map);
1050 } else {
1051 ret = asprintf(&path, "%s/%s", AUTO_MAP_PREFIX, map);
1052 if (ret < 0)
1053 log_err(1, "asprintf");
1054 log_debugx("map \"%s\" maps to \"%s\"", map, path);
1055
1056 /*
1057 * See if the file exists. If not, try to obtain the map
1058 * from directory services.
1059 */
1060 error = access(path, F_OK);
1061 if (error != 0) {
1062 log_debugx("map file \"%s\" does not exist; falling "
1063 "back to directory services", path);
1064 return (parse_included_map(parent, map));
1065 }
1066 }
1067
1068 executable = file_is_executable(path);
1069
1070 if (executable) {
1071 log_debugx("map \"%s\" is executable", map);
1072
1073 if (wildcards != NULL)
1074 *wildcards = true;
1075
1076 if (key != NULL) {
1077 yyin = auto_popen(path, key, NULL);
1078 } else {
1079 yyin = auto_popen(path, NULL);
1080 }
1081 assert(yyin != NULL);
1082 } else {
1083 yyin = fopen(path, "r");
1084 if (yyin == NULL)
1085 log_err(1, "unable to open \"%s\"", path);
1086 }
1087
1088 free(path);
1089 path = NULL;
1090
1091 parse_map_yyin(parent, map, executable ? key : NULL);
1092
1093 if (executable) {
1094 error = auto_pclose(yyin);
1095 yyin = NULL;
1096 if (error != 0) {
1097 log_errx(1, "failed to handle executable map \"%s\"",
1098 map);
1099 }
1100 } else {
1101 fclose(yyin);
1102 }
1103 yyin = NULL;
1104
1105 log_debugx("done parsing map \"%s\"", map);
1106
1107 node_expand_includes(parent, false);
1108 node_expand_direct_maps(parent);
1109 }
1110
1111 static void
parse_master_yyin(struct node * root,const char * master)1112 parse_master_yyin(struct node *root, const char *master)
1113 {
1114 char *mountpoint = NULL, *map = NULL, *options = NULL;
1115 int ret;
1116
1117 /*
1118 * XXX: 1 gives incorrect values; wtf?
1119 */
1120 lineno = 0;
1121
1122 for (;;) {
1123 ret = yylex();
1124 if (ret == 0 || ret == NEWLINE) {
1125 if (mountpoint != NULL) {
1126 //log_debugx("adding map for %s", mountpoint);
1127 node_new_map(root, mountpoint, options, map,
1128 master, lineno);
1129 }
1130 if (ret == 0) {
1131 break;
1132 } else {
1133 mountpoint = map = options = NULL;
1134 continue;
1135 }
1136 }
1137 if (mountpoint == NULL) {
1138 mountpoint = checked_strdup(yytext);
1139 } else if (map == NULL) {
1140 map = checked_strdup(yytext);
1141 } else if (options == NULL) {
1142 /*
1143 * +1 to skip leading "-".
1144 */
1145 options = checked_strdup(yytext + 1);
1146 } else {
1147 log_errx(1, "too many arguments at %s, line %d",
1148 master, lineno);
1149 }
1150 }
1151 }
1152
1153 void
parse_master(struct node * root,const char * master)1154 parse_master(struct node *root, const char *master)
1155 {
1156
1157 log_debugx("parsing auto_master file at \"%s\"", master);
1158
1159 yyin = fopen(master, "r");
1160 if (yyin == NULL)
1161 err(1, "unable to open %s", master);
1162
1163 parse_master_yyin(root, master);
1164
1165 fclose(yyin);
1166 yyin = NULL;
1167
1168 log_debugx("done parsing \"%s\"", master);
1169
1170 node_expand_includes(root, true);
1171 node_expand_direct_maps(root);
1172 }
1173
1174 /*
1175 * Two things daemon(3) does, that we actually also want to do
1176 * when running in foreground, is closing the stdin and chdiring
1177 * to "/". This is what we do here.
1178 */
1179 void
lesser_daemon(void)1180 lesser_daemon(void)
1181 {
1182 int error, fd;
1183
1184 error = chdir("/");
1185 if (error != 0)
1186 log_warn("chdir");
1187
1188 fd = open(_PATH_DEVNULL, O_RDWR, 0);
1189 if (fd < 0) {
1190 log_warn("cannot open %s", _PATH_DEVNULL);
1191 return;
1192 }
1193
1194 error = dup2(fd, STDIN_FILENO);
1195 if (error != 0)
1196 log_warn("dup2");
1197
1198 error = close(fd);
1199 if (error != 0) {
1200 /* Bloody hell. */
1201 log_warn("close");
1202 }
1203 }
1204
1205 /*
1206 * Applicable to NFSv3 only, see rpc.umntall(8).
1207 */
1208 void
rpc_umntall(void)1209 rpc_umntall(void)
1210 {
1211 FILE *f;
1212
1213 f = auto_popen("rpc.umntall", "-k", NULL);
1214 assert(f != NULL);
1215 auto_pclose(f);
1216 }
1217
1218 int
main(int argc,char ** argv)1219 main(int argc, char **argv)
1220 {
1221 char *cmdname;
1222
1223 if (argv[0] == NULL)
1224 log_errx(1, "NULL command name");
1225
1226 cmdname = basename(argv[0]);
1227
1228 if (strcmp(cmdname, "automount") == 0)
1229 return (main_automount(argc, argv));
1230 else if (strcmp(cmdname, "automountd") == 0)
1231 return (main_automountd(argc, argv));
1232 else if (strcmp(cmdname, "autounmountd") == 0)
1233 return (main_autounmountd(argc, argv));
1234 else
1235 log_errx(1, "binary name should be either \"automount\", "
1236 "\"automountd\", or \"autounmountd\"");
1237 }
1238