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