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