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