]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/autofs/common.c
simd(7): add missing aarch64 SIMD functions
[FreeBSD/FreeBSD.git] / usr.sbin / autofs / common.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2014 The FreeBSD Foundation
5  *
6  * This software was developed by Edward Tomasz Napierala under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/ioctl.h>
35 #include <sys/param.h>
36 #include <sys/linker.h>
37 #include <sys/mount.h>
38 #include <sys/socket.h>
39 #include <sys/stat.h>
40 #include <sys/wait.h>
41 #include <sys/utsname.h>
42 #include <assert.h>
43 #include <ctype.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <libgen.h>
48 #include <libutil.h>
49 #include <netdb.h>
50 #include <paths.h>
51 #include <signal.h>
52 #include <stdbool.h>
53 #include <stdint.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58
59 #include "autofs_ioctl.h"
60
61 #include "common.h"
62
63 extern FILE *yyin;
64 extern char *yytext;
65 extern int yylex(void);
66
67 static void     parse_master_yyin(struct node *root, const char *master);
68 static void     parse_map_yyin(struct node *parent, const char *map,
69                     const char *executable_key);
70
71 char *
72 checked_strdup(const char *s)
73 {
74         char *c;
75
76         assert(s != NULL);
77
78         c = strdup(s);
79         if (c == NULL)
80                 log_err(1, "strdup");
81         return (c);
82 }
83
84 /*
85  * Concatenate two strings, inserting separator between them, unless not needed.
86  */
87 char *
88 concat(const char *s1, char separator, const char *s2)
89 {
90         char *result;
91         char s1last, s2first;
92         int ret;
93
94         if (s1 == NULL)
95                 s1 = "";
96         if (s2 == NULL)
97                 s2 = "";
98
99         if (s1[0] == '\0')
100                 s1last = '\0';
101         else
102                 s1last = s1[strlen(s1) - 1];
103
104         s2first = s2[0];
105
106         if (s1last == separator && s2first == separator) {
107                 /*
108                  * If s1 ends with the separator and s2 begins with
109                  * it - skip the latter; otherwise concatenating "/"
110                  * and "/foo" would end up returning "//foo".
111                  */
112                 ret = asprintf(&result, "%s%s", s1, s2 + 1);
113         } else if (s1last == separator || s2first == separator ||
114             s1[0] == '\0' || s2[0] == '\0') {
115                 ret = asprintf(&result, "%s%s", s1, s2);
116         } else {
117                 ret = asprintf(&result, "%s%c%s", s1, separator, s2);
118         }
119         if (ret < 0)
120                 log_err(1, "asprintf");
121
122         //log_debugx("%s: got %s and %s, returning %s", __func__, s1, s2, result);
123
124         return (result);
125 }
126
127 void
128 create_directory(const char *path)
129 {
130         char *component, *copy, *tofree, *partial, *tmp;
131         int error;
132
133         assert(path[0] == '/');
134
135         /*
136          * +1 to skip the leading slash.
137          */
138         copy = tofree = checked_strdup(path + 1);
139
140         partial = checked_strdup("/");
141         for (;;) {
142                 component = strsep(&copy, "/");
143                 if (component == NULL)
144                         break;
145                 tmp = concat(partial, '/', component);
146                 free(partial);
147                 partial = tmp;
148                 //log_debugx("creating \"%s\"", partial);
149                 error = mkdir(partial, 0755);
150                 if (error != 0 && errno != EEXIST) {
151                         log_warn("cannot create %s", partial);
152                         return;
153                 }
154         }
155
156         free(tofree);
157 }
158
159 struct node *
160 node_new_root(void)
161 {
162         struct node *n;
163
164         n = calloc(1, sizeof(*n));
165         if (n == NULL)
166                 log_err(1, "calloc");
167         // XXX
168         n->n_key = checked_strdup("/");
169         n->n_options = checked_strdup("");
170
171         TAILQ_INIT(&n->n_children);
172
173         return (n);
174 }
175
176 struct node *
177 node_new(struct node *parent, char *key, char *options, char *location,
178     const char *config_file, int config_line)
179 {
180         struct node *n;
181
182         n = calloc(1, sizeof(*n));
183         if (n == NULL)
184                 log_err(1, "calloc");
185
186         TAILQ_INIT(&n->n_children);
187         assert(key != NULL);
188         assert(key[0] != '\0');
189         n->n_key = key;
190         if (options != NULL)
191                 n->n_options = options;
192         else
193                 n->n_options = strdup("");
194         n->n_location = location;
195         assert(config_file != NULL);
196         n->n_config_file = config_file;
197         assert(config_line >= 0);
198         n->n_config_line = config_line;
199
200         assert(parent != NULL);
201         n->n_parent = parent;
202         TAILQ_INSERT_TAIL(&parent->n_children, n, n_next);
203
204         return (n);
205 }
206
207 struct node *
208 node_new_map(struct node *parent, char *key, char *options, char *map,
209     const char *config_file, int config_line)
210 {
211         struct node *n;
212
213         n = calloc(1, sizeof(*n));
214         if (n == NULL)
215                 log_err(1, "calloc");
216
217         TAILQ_INIT(&n->n_children);
218         assert(key != NULL);
219         assert(key[0] != '\0');
220         n->n_key = key;
221         if (options != NULL)
222                 n->n_options = options;
223         else
224                 n->n_options = strdup("");
225         n->n_map = map;
226         assert(config_file != NULL);
227         n->n_config_file = config_file;
228         assert(config_line >= 0);
229         n->n_config_line = config_line;
230
231         assert(parent != NULL);
232         n->n_parent = parent;
233         TAILQ_INSERT_TAIL(&parent->n_children, n, n_next);
234
235         return (n);
236 }
237
238 static struct node *
239 node_duplicate(const struct node *o, struct node *parent)
240 {
241         const struct node *child;
242         struct node *n;
243
244         if (parent == NULL)
245                 parent = o->n_parent;
246
247         n = node_new(parent, o->n_key, o->n_options, o->n_location,
248             o->n_config_file, o->n_config_line);
249
250         TAILQ_FOREACH(child, &o->n_children, n_next)
251                 node_duplicate(child, n);
252
253         return (n);
254 }
255
256 static void
257 node_delete(struct node *n)
258 {
259         struct node *child, *tmp;
260
261         assert (n != NULL);
262
263         TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp)
264                 node_delete(child);
265
266         if (n->n_parent != NULL)
267                 TAILQ_REMOVE(&n->n_parent->n_children, n, n_next);
268
269         free(n);
270 }
271
272 /*
273  * Move (reparent) node 'n' to make it sibling of 'previous', placed
274  * just after it.
275  */
276 static void
277 node_move_after(struct node *n, struct node *previous)
278 {
279
280         TAILQ_REMOVE(&n->n_parent->n_children, n, n_next);
281         n->n_parent = previous->n_parent;
282         TAILQ_INSERT_AFTER(&previous->n_parent->n_children, previous, n, n_next);
283 }
284
285 static void
286 node_expand_includes(struct node *root, bool is_master)
287 {
288         struct node *n, *n2, *tmp, *tmp2, *tmproot;
289         int error;
290
291         TAILQ_FOREACH_SAFE(n, &root->n_children, n_next, tmp) {
292                 if (n->n_key[0] != '+')
293                         continue;
294
295                 error = access(AUTO_INCLUDE_PATH, F_OK);
296                 if (error != 0) {
297                         log_errx(1, "directory services not configured; "
298                             "%s does not exist", AUTO_INCLUDE_PATH);
299                 }
300
301                 /*
302                  * "+1" to skip leading "+".
303                  */
304                 yyin = auto_popen(AUTO_INCLUDE_PATH, n->n_key + 1, NULL);
305                 assert(yyin != NULL);
306
307                 tmproot = node_new_root();
308                 if (is_master)
309                         parse_master_yyin(tmproot, n->n_key);
310                 else
311                         parse_map_yyin(tmproot, n->n_key, NULL);
312
313                 error = auto_pclose(yyin);
314                 yyin = NULL;
315                 if (error != 0) {
316                         log_errx(1, "failed to handle include \"%s\"",
317                             n->n_key);
318                 }
319
320                 /*
321                  * Entries to be included are now in tmproot.  We need to merge
322                  * them with the rest, preserving their place and ordering.
323                  */
324                 TAILQ_FOREACH_REVERSE_SAFE(n2,
325                     &tmproot->n_children, nodehead, n_next, tmp2) {
326                         node_move_after(n2, n);
327                 }
328
329                 node_delete(n);
330                 node_delete(tmproot);
331         }
332 }
333
334 static char *
335 expand_ampersand(char *string, const char *key)
336 {
337         char c, *expanded;
338         int i, ret, before_len = 0;
339         bool backslashed = false;
340
341         assert(key[0] != '\0');
342
343         expanded = checked_strdup(string);
344
345         for (i = 0; string[i] != '\0'; i++) {
346                 c = string[i];
347                 if (c == '\\' && backslashed == false) {
348                         backslashed = true;
349                         continue;
350                 }
351                 if (backslashed) {
352                         backslashed = false;
353                         continue;
354                 }
355                 backslashed = false;
356                 if (c != '&')
357                         continue;
358
359                 /*
360                  * The 'before_len' variable contains the number
361                  * of characters before the '&'.
362                  */
363                 before_len = i;
364                 //assert(i < (int)strlen(string));
365
366                 ret = asprintf(&expanded, "%.*s%s%s",
367                     before_len, string, key, string + before_len + 1);
368                 if (ret < 0)
369                         log_err(1, "asprintf");
370
371                 //log_debugx("\"%s\" expanded with key \"%s\" to \"%s\"",
372                 //    string, key, expanded);
373
374                 /*
375                  * Figure out where to start searching for next variable.
376                  */
377                 string = expanded;
378                 i = before_len + strlen(key);
379                 if (i == (int)strlen(string))
380                         break;
381                 backslashed = false;
382                 //assert(i < (int)strlen(string));
383         }
384
385         return (expanded);
386 }
387
388 /*
389  * Expand "&" in n_location.  If the key is NULL, try to use
390  * key from map entries themselves.  Keep in mind that maps
391  * consist of tho levels of node structures, the key is one
392  * level up.
393  *
394  * Variant with NULL key is for "automount -LL".
395  */
396 void
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 /*
1205  * Applicable to NFSv3 only, see rpc.umntall(8).
1206  */
1207 void
1208 rpc_umntall(void)
1209 {
1210         FILE *f;
1211
1212         f = auto_popen("rpc.umntall", "-k", NULL);
1213         assert(f != NULL);
1214         auto_pclose(f);
1215 }
1216
1217 int
1218 main(int argc, char **argv)
1219 {
1220         char *cmdname;
1221
1222         if (argv[0] == NULL)
1223                 log_errx(1, "NULL command name");
1224
1225         cmdname = basename(argv[0]);
1226
1227         if (strcmp(cmdname, "automount") == 0)
1228                 return (main_automount(argc, argv));
1229         else if (strcmp(cmdname, "automountd") == 0)
1230                 return (main_automountd(argc, argv));
1231         else if (strcmp(cmdname, "autounmountd") == 0)
1232                 return (main_autounmountd(argc, argv));
1233         else
1234                 log_errx(1, "binary name should be either \"automount\", "
1235                     "\"automountd\", or \"autounmountd\"");
1236 }