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