]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/autofs/common.c
Merge llvm trunk r321414 to contrib/llvm.
[FreeBSD/FreeBSD.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 <libutil.h>
51 #include <netdb.h>
52 #include <paths.h>
53 #include <signal.h>
54 #include <stdbool.h>
55 #include <stdint.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include "autofs_ioctl.h"
62
63 #include "common.h"
64
65 extern FILE *yyin;
66 extern char *yytext;
67 extern int yylex(void);
68
69 static void     parse_master_yyin(struct node *root, const char *master);
70 static void     parse_map_yyin(struct node *parent, const char *map,
71                     const char *executable_key);
72
73 char *
74 checked_strdup(const char *s)
75 {
76         char *c;
77
78         assert(s != NULL);
79
80         c = strdup(s);
81         if (c == NULL)
82                 log_err(1, "strdup");
83         return (c);
84 }
85
86 /*
87  * Concatenate two strings, inserting separator between them, unless not needed.
88  */
89 char *
90 concat(const char *s1, char separator, const char *s2)
91 {
92         char *result;
93         char s1last, s2first;
94         int ret;
95
96         if (s1 == NULL)
97                 s1 = "";
98         if (s2 == NULL)
99                 s2 = "";
100
101         if (s1[0] == '\0')
102                 s1last = '\0';
103         else
104                 s1last = s1[strlen(s1) - 1];
105
106         s2first = s2[0];
107
108         if (s1last == separator && s2first == separator) {
109                 /*
110                  * If s1 ends with the separator and s2 begins with
111                  * it - skip the latter; otherwise concatenating "/"
112                  * and "/foo" would end up returning "//foo".
113                  */
114                 ret = asprintf(&result, "%s%s", s1, s2 + 1);
115         } else if (s1last == separator || s2first == separator ||
116             s1[0] == '\0' || s2[0] == '\0') {
117                 ret = asprintf(&result, "%s%s", s1, s2);
118         } else {
119                 ret = asprintf(&result, "%s%c%s", s1, separator, s2);
120         }
121         if (ret < 0)
122                 log_err(1, "asprintf");
123
124         //log_debugx("%s: got %s and %s, returning %s", __func__, s1, s2, result);
125
126         return (result);
127 }
128
129 void
130 create_directory(const char *path)
131 {
132         char *component, *copy, *tofree, *partial, *tmp;
133         int error;
134
135         assert(path[0] == '/');
136
137         /*
138          * +1 to skip the leading slash.
139          */
140         copy = tofree = checked_strdup(path + 1);
141
142         partial = checked_strdup("");
143         for (;;) {
144                 component = strsep(&copy, "/");
145                 if (component == NULL)
146                         break;
147                 tmp = concat(partial, '/', component);
148                 free(partial);
149                 partial = tmp;
150                 //log_debugx("creating \"%s\"", partial);
151                 error = mkdir(partial, 0755);
152                 if (error != 0 && errno != EEXIST) {
153                         log_warn("cannot create %s", partial);
154                         return;
155                 }
156         }
157
158         free(tofree);
159 }
160
161 struct node *
162 node_new_root(void)
163 {
164         struct node *n;
165
166         n = calloc(1, sizeof(*n));
167         if (n == NULL)
168                 log_err(1, "calloc");
169         // XXX
170         n->n_key = checked_strdup("/");
171         n->n_options = checked_strdup("");
172
173         TAILQ_INIT(&n->n_children);
174
175         return (n);
176 }
177
178 struct node *
179 node_new(struct node *parent, char *key, char *options, char *location,
180     const char *config_file, int config_line)
181 {
182         struct node *n;
183
184         n = calloc(1, sizeof(*n));
185         if (n == NULL)
186                 log_err(1, "calloc");
187
188         TAILQ_INIT(&n->n_children);
189         assert(key != NULL);
190         assert(key[0] != '\0');
191         n->n_key = key;
192         if (options != NULL)
193                 n->n_options = options;
194         else
195                 n->n_options = strdup("");
196         n->n_location = location;
197         assert(config_file != NULL);
198         n->n_config_file = config_file;
199         assert(config_line >= 0);
200         n->n_config_line = config_line;
201
202         assert(parent != NULL);
203         n->n_parent = parent;
204         TAILQ_INSERT_TAIL(&parent->n_children, n, n_next);
205
206         return (n);
207 }
208
209 struct node *
210 node_new_map(struct node *parent, char *key, char *options, char *map,
211     const char *config_file, int config_line)
212 {
213         struct node *n;
214
215         n = calloc(1, sizeof(*n));
216         if (n == NULL)
217                 log_err(1, "calloc");
218
219         TAILQ_INIT(&n->n_children);
220         assert(key != NULL);
221         assert(key[0] != '\0');
222         n->n_key = key;
223         if (options != NULL)
224                 n->n_options = options;
225         else
226                 n->n_options = strdup("");
227         n->n_map = map;
228         assert(config_file != NULL);
229         n->n_config_file = config_file;
230         assert(config_line >= 0);
231         n->n_config_line = config_line;
232
233         assert(parent != NULL);
234         n->n_parent = parent;
235         TAILQ_INSERT_TAIL(&parent->n_children, n, n_next);
236
237         return (n);
238 }
239
240 static struct node *
241 node_duplicate(const struct node *o, struct node *parent)
242 {
243         const struct node *child;
244         struct node *n;
245
246         if (parent == NULL)
247                 parent = o->n_parent;
248
249         n = node_new(parent, o->n_key, o->n_options, o->n_location,
250             o->n_config_file, o->n_config_line);
251
252         TAILQ_FOREACH(child, &o->n_children, n_next)
253                 node_duplicate(child, n);
254
255         return (n);
256 }
257
258 static void
259 node_delete(struct node *n)
260 {
261         struct node *child, *tmp;
262
263         assert (n != NULL);
264
265         TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp)
266                 node_delete(child);
267
268         if (n->n_parent != NULL)
269                 TAILQ_REMOVE(&n->n_parent->n_children, n, n_next);
270
271         free(n);
272 }
273
274 /*
275  * Move (reparent) node 'n' to make it sibling of 'previous', placed
276  * just after it.
277  */
278 static void
279 node_move_after(struct node *n, struct node *previous)
280 {
281
282         TAILQ_REMOVE(&n->n_parent->n_children, n, n_next);
283         n->n_parent = previous->n_parent;
284         TAILQ_INSERT_AFTER(&previous->n_parent->n_children, previous, n, n_next);
285 }
286
287 static void
288 node_expand_includes(struct node *root, bool is_master)
289 {
290         struct node *n, *n2, *tmp, *tmp2, *tmproot;
291         int error;
292
293         TAILQ_FOREACH_SAFE(n, &root->n_children, n_next, tmp) {
294                 if (n->n_key[0] != '+')
295                         continue;
296
297                 error = access(AUTO_INCLUDE_PATH, F_OK);
298                 if (error != 0) {
299                         log_errx(1, "directory services not configured; "
300                             "%s does not exist", AUTO_INCLUDE_PATH);
301                 }
302
303                 /*
304                  * "+1" to skip leading "+".
305                  */
306                 yyin = auto_popen(AUTO_INCLUDE_PATH, n->n_key + 1, NULL);
307                 assert(yyin != NULL);
308
309                 tmproot = node_new_root();
310                 if (is_master)
311                         parse_master_yyin(tmproot, n->n_key);
312                 else
313                         parse_map_yyin(tmproot, n->n_key, NULL);
314
315                 error = auto_pclose(yyin);
316                 yyin = NULL;
317                 if (error != 0) {
318                         log_errx(1, "failed to handle include \"%s\"",
319                             n->n_key);
320                 }
321
322                 /*
323                  * Entries to be included are now in tmproot.  We need to merge
324                  * them with the rest, preserving their place and ordering.
325                  */
326                 TAILQ_FOREACH_REVERSE_SAFE(n2,
327                     &tmproot->n_children, nodehead, n_next, tmp2) {
328                         node_move_after(n2, n);
329                 }
330
331                 node_delete(n);
332                 node_delete(tmproot);
333         }
334 }
335
336 static char *
337 expand_ampersand(char *string, const char *key)
338 {
339         char c, *expanded;
340         int i, ret, before_len = 0;
341         bool backslashed = false;
342
343         assert(key[0] != '\0');
344
345         expanded = checked_strdup(string);
346
347         for (i = 0; string[i] != '\0'; i++) {
348                 c = string[i];
349                 if (c == '\\' && backslashed == false) {
350                         backslashed = true;
351                         continue;
352                 }
353                 if (backslashed) {
354                         backslashed = false;
355                         continue;
356                 }
357                 backslashed = false;
358                 if (c != '&')
359                         continue;
360
361                 /*
362                  * The 'before_len' variable contains the number
363                  * of characters before the '&'.
364                  */
365                 before_len = i;
366                 //assert(i + 1 < (int)strlen(string));
367
368                 ret = asprintf(&expanded, "%.*s%s%s",
369                     before_len, string, key, string + before_len + 1);
370                 if (ret < 0)
371                         log_err(1, "asprintf");
372
373                 //log_debugx("\"%s\" expanded with key \"%s\" to \"%s\"",
374                 //    string, key, expanded);
375
376                 /*
377                  * Figure out where to start searching for next variable.
378                  */
379                 string = expanded;
380                 i = before_len + strlen(key);
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
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
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
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
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
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
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
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
533 node_expand_direct_maps(struct node *n)
534 {
535
536         node_expand_maps(n, false);
537 }
538
539 void
540 node_expand_indirect_maps(struct node *n)
541 {
542
543         node_expand_maps(n, true);
544 }
545
546 static char *
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 *
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 *
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 *
610 node_options(const struct node *n)
611 {
612
613         return (node_options_x(n, checked_strdup("")));
614 }
615
616 static void
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
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 *
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 *
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
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
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
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
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
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
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
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
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
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 int
1205 main(int argc, char **argv)
1206 {
1207         char *cmdname;
1208
1209         if (argv[0] == NULL)
1210                 log_errx(1, "NULL command name");
1211
1212         cmdname = basename(argv[0]);
1213
1214         if (strcmp(cmdname, "automount") == 0)
1215                 return (main_automount(argc, argv));
1216         else if (strcmp(cmdname, "automountd") == 0)
1217                 return (main_automountd(argc, argv));
1218         else if (strcmp(cmdname, "autounmountd") == 0)
1219                 return (main_autounmountd(argc, argv));
1220         else
1221                 log_errx(1, "binary name should be either \"automount\", "
1222                     "\"automountd\", or \"autounmountd\"");
1223 }