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