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