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