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