1270096Strasz/*- 2270096Strasz * Copyright (c) 2014 The FreeBSD Foundation 3270096Strasz * All rights reserved. 4270096Strasz * 5270096Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6270096Strasz * from the FreeBSD Foundation. 7270096Strasz * 8270096Strasz * Redistribution and use in source and binary forms, with or without 9270096Strasz * modification, are permitted provided that the following conditions 10270096Strasz * are met: 11270096Strasz * 1. Redistributions of source code must retain the above copyright 12270096Strasz * notice, this list of conditions and the following disclaimer. 13270096Strasz * 2. Redistributions in binary form must reproduce the above copyright 14270096Strasz * notice, this list of conditions and the following disclaimer in the 15270096Strasz * documentation and/or other materials provided with the distribution. 16270096Strasz * 17270096Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20270096Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27270096Strasz * SUCH DAMAGE. 28270096Strasz * 29270096Strasz */ 30270096Strasz 31270897Strasz#include <sys/cdefs.h> 32270897Strasz__FBSDID("$FreeBSD: releng/10.3/usr.sbin/autofs/common.c 283240 2015-05-21 13:39:38Z trasz $"); 33270897Strasz 34270096Strasz#include <sys/types.h> 35270096Strasz#include <sys/time.h> 36270096Strasz#include <sys/ioctl.h> 37270096Strasz#include <sys/param.h> 38270096Strasz#include <sys/linker.h> 39270096Strasz#include <sys/mount.h> 40270096Strasz#include <sys/socket.h> 41270096Strasz#include <sys/stat.h> 42270096Strasz#include <sys/wait.h> 43270096Strasz#include <sys/utsname.h> 44270096Strasz#include <assert.h> 45270096Strasz#include <ctype.h> 46270096Strasz#include <err.h> 47270096Strasz#include <errno.h> 48270096Strasz#include <fcntl.h> 49270096Strasz#include <libgen.h> 50270096Strasz#include <netdb.h> 51270096Strasz#include <paths.h> 52270096Strasz#include <signal.h> 53270096Strasz#include <stdbool.h> 54270096Strasz#include <stdint.h> 55270903Strasz#define _WITH_GETLINE 56270096Strasz#include <stdio.h> 57270096Strasz#include <stdlib.h> 58270096Strasz#include <string.h> 59270096Strasz#include <unistd.h> 60270096Strasz 61270096Strasz#include <libutil.h> 62270096Strasz 63270096Strasz#include "autofs_ioctl.h" 64270096Strasz 65270096Strasz#include "common.h" 66270096Strasz 67270096Straszextern FILE *yyin; 68270096Straszextern char *yytext; 69270096Straszextern int yylex(void); 70270096Strasz 71270096Straszstatic void parse_master_yyin(struct node *root, const char *master); 72270096Straszstatic void parse_map_yyin(struct node *parent, const char *map, 73270096Strasz const char *executable_key); 74270096Strasz 75270096Straszchar * 76270096Straszchecked_strdup(const char *s) 77270096Strasz{ 78270096Strasz char *c; 79270096Strasz 80270096Strasz assert(s != NULL); 81270096Strasz 82270096Strasz c = strdup(s); 83270096Strasz if (c == NULL) 84270096Strasz log_err(1, "strdup"); 85270096Strasz return (c); 86270096Strasz} 87270096Strasz 88270096Strasz/* 89270096Strasz * Concatenate two strings, inserting separator between them, unless not needed. 90270096Strasz */ 91270096Straszchar * 92283231Straszconcat(const char *s1, char separator, const char *s2) 93270096Strasz{ 94270096Strasz char *result; 95283240Strasz char s1last, s2first; 96270096Strasz int ret; 97270096Strasz 98283238Strasz if (s1 == NULL) 99283238Strasz s1 = ""; 100283238Strasz if (s2 == NULL) 101283238Strasz s2 = ""; 102270096Strasz 103283240Strasz if (s1[0] == '\0') 104283240Strasz s1last = '\0'; 105283240Strasz else 106283240Strasz s1last = s1[strlen(s1) - 1]; 107283228Strasz 108283240Strasz s2first = s2[0]; 109283240Strasz 110283240Strasz if (s1last == separator && s2first == separator) { 111283240Strasz /* 112283240Strasz * If s1 ends with the separator and s2 begins with 113283240Strasz * it - skip the latter; otherwise concatenating "/" 114283240Strasz * and "/foo" would end up returning "//foo". 115283240Strasz */ 116283240Strasz ret = asprintf(&result, "%s%s", s1, s2 + 1); 117283240Strasz } else if (s1last == separator || s2first == separator || 118283240Strasz s1[0] == '\0' || s2[0] == '\0') { 119270096Strasz ret = asprintf(&result, "%s%s", s1, s2); 120270096Strasz } else { 121270096Strasz ret = asprintf(&result, "%s%c%s", s1, separator, s2); 122270096Strasz } 123270096Strasz if (ret < 0) 124270096Strasz log_err(1, "asprintf"); 125270096Strasz 126283230Strasz //log_debugx("%s: got %s and %s, returning %s", __func__, s1, s2, result); 127270096Strasz 128270096Strasz return (result); 129270096Strasz} 130270096Strasz 131270096Straszvoid 132270096Straszcreate_directory(const char *path) 133270096Strasz{ 134283230Strasz char *component, *copy, *tofree, *partial, *tmp; 135270096Strasz int error; 136270096Strasz 137270096Strasz assert(path[0] == '/'); 138270096Strasz 139270096Strasz /* 140270096Strasz * +1 to skip the leading slash. 141270096Strasz */ 142270096Strasz copy = tofree = checked_strdup(path + 1); 143270096Strasz 144283230Strasz partial = checked_strdup(""); 145270096Strasz for (;;) { 146270096Strasz component = strsep(©, "/"); 147270096Strasz if (component == NULL) 148270096Strasz break; 149283231Strasz tmp = concat(partial, '/', component); 150283230Strasz free(partial); 151283230Strasz partial = tmp; 152274500Strasz //log_debugx("creating \"%s\"", partial); 153270096Strasz error = mkdir(partial, 0755); 154274500Strasz if (error != 0 && errno != EEXIST) { 155274500Strasz log_warn("cannot create %s", partial); 156274500Strasz return; 157274500Strasz } 158270096Strasz } 159270096Strasz 160270096Strasz free(tofree); 161270096Strasz} 162270096Strasz 163270096Straszstruct node * 164270096Strasznode_new_root(void) 165270096Strasz{ 166270096Strasz struct node *n; 167270096Strasz 168270096Strasz n = calloc(1, sizeof(*n)); 169270096Strasz if (n == NULL) 170270096Strasz log_err(1, "calloc"); 171270096Strasz // XXX 172270096Strasz n->n_key = checked_strdup("/"); 173270096Strasz n->n_options = checked_strdup(""); 174270096Strasz 175270096Strasz TAILQ_INIT(&n->n_children); 176270096Strasz 177270096Strasz return (n); 178270096Strasz} 179270096Strasz 180270096Straszstruct node * 181270096Strasznode_new(struct node *parent, char *key, char *options, char *location, 182270096Strasz const char *config_file, int config_line) 183270096Strasz{ 184270096Strasz struct node *n; 185270096Strasz 186270096Strasz n = calloc(1, sizeof(*n)); 187270096Strasz if (n == NULL) 188270096Strasz log_err(1, "calloc"); 189270096Strasz 190270096Strasz TAILQ_INIT(&n->n_children); 191270096Strasz assert(key != NULL); 192270903Strasz assert(key[0] != '\0'); 193270096Strasz n->n_key = key; 194270096Strasz if (options != NULL) 195270096Strasz n->n_options = options; 196270096Strasz else 197270096Strasz n->n_options = strdup(""); 198270096Strasz n->n_location = location; 199270096Strasz assert(config_file != NULL); 200270096Strasz n->n_config_file = config_file; 201270096Strasz assert(config_line >= 0); 202270096Strasz n->n_config_line = config_line; 203270096Strasz 204270096Strasz assert(parent != NULL); 205270096Strasz n->n_parent = parent; 206270096Strasz TAILQ_INSERT_TAIL(&parent->n_children, n, n_next); 207270096Strasz 208270096Strasz return (n); 209270096Strasz} 210270096Strasz 211270096Straszstruct node * 212270096Strasznode_new_map(struct node *parent, char *key, char *options, char *map, 213270096Strasz const char *config_file, int config_line) 214270096Strasz{ 215270096Strasz struct node *n; 216270096Strasz 217270096Strasz n = calloc(1, sizeof(*n)); 218270096Strasz if (n == NULL) 219270096Strasz log_err(1, "calloc"); 220270096Strasz 221270096Strasz TAILQ_INIT(&n->n_children); 222270096Strasz assert(key != NULL); 223270903Strasz assert(key[0] != '\0'); 224270096Strasz n->n_key = key; 225270096Strasz if (options != NULL) 226270096Strasz n->n_options = options; 227270096Strasz else 228270096Strasz n->n_options = strdup(""); 229270096Strasz n->n_map = map; 230270096Strasz assert(config_file != NULL); 231270096Strasz n->n_config_file = config_file; 232270096Strasz assert(config_line >= 0); 233270096Strasz n->n_config_line = config_line; 234270096Strasz 235270096Strasz assert(parent != NULL); 236270096Strasz n->n_parent = parent; 237270096Strasz TAILQ_INSERT_TAIL(&parent->n_children, n, n_next); 238270096Strasz 239270096Strasz return (n); 240270096Strasz} 241270096Strasz 242270096Straszstatic struct node * 243270096Strasznode_duplicate(const struct node *o, struct node *parent) 244270096Strasz{ 245270096Strasz const struct node *child; 246270096Strasz struct node *n; 247270096Strasz 248270096Strasz if (parent == NULL) 249270096Strasz parent = o->n_parent; 250270096Strasz 251270096Strasz n = node_new(parent, o->n_key, o->n_options, o->n_location, 252270096Strasz o->n_config_file, o->n_config_line); 253270096Strasz 254270096Strasz TAILQ_FOREACH(child, &o->n_children, n_next) 255270096Strasz node_duplicate(child, n); 256270096Strasz 257270096Strasz return (n); 258270096Strasz} 259270096Strasz 260270096Straszstatic void 261270096Strasznode_delete(struct node *n) 262270096Strasz{ 263270096Strasz struct node *child, *tmp; 264270096Strasz 265270096Strasz assert (n != NULL); 266270096Strasz 267270096Strasz TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) 268270096Strasz node_delete(child); 269270096Strasz 270270096Strasz if (n->n_parent != NULL) 271270096Strasz TAILQ_REMOVE(&n->n_parent->n_children, n, n_next); 272270096Strasz 273270096Strasz free(n); 274270096Strasz} 275270096Strasz 276270096Strasz/* 277270096Strasz * Move (reparent) node 'n' to make it sibling of 'previous', placed 278270096Strasz * just after it. 279270096Strasz */ 280270096Straszstatic void 281270096Strasznode_move_after(struct node *n, struct node *previous) 282270096Strasz{ 283270096Strasz 284270096Strasz TAILQ_REMOVE(&n->n_parent->n_children, n, n_next); 285270096Strasz n->n_parent = previous->n_parent; 286270096Strasz TAILQ_INSERT_AFTER(&previous->n_parent->n_children, previous, n, n_next); 287270096Strasz} 288270096Strasz 289270096Straszstatic void 290270096Strasznode_expand_includes(struct node *root, bool is_master) 291270096Strasz{ 292270096Strasz struct node *n, *n2, *tmp, *tmp2, *tmproot; 293270096Strasz int error; 294270096Strasz 295270096Strasz TAILQ_FOREACH_SAFE(n, &root->n_children, n_next, tmp) { 296270096Strasz if (n->n_key[0] != '+') 297270096Strasz continue; 298270096Strasz 299270096Strasz error = access(AUTO_INCLUDE_PATH, F_OK); 300270096Strasz if (error != 0) { 301270096Strasz log_errx(1, "directory services not configured; " 302270096Strasz "%s does not exist", AUTO_INCLUDE_PATH); 303270096Strasz } 304270096Strasz 305270096Strasz /* 306270096Strasz * "+1" to skip leading "+". 307270096Strasz */ 308270096Strasz yyin = auto_popen(AUTO_INCLUDE_PATH, n->n_key + 1, NULL); 309270096Strasz assert(yyin != NULL); 310270096Strasz 311270096Strasz tmproot = node_new_root(); 312270096Strasz if (is_master) 313270096Strasz parse_master_yyin(tmproot, n->n_key); 314270096Strasz else 315270096Strasz parse_map_yyin(tmproot, n->n_key, NULL); 316270096Strasz 317270096Strasz error = auto_pclose(yyin); 318270096Strasz yyin = NULL; 319270096Strasz if (error != 0) { 320270096Strasz log_errx(1, "failed to handle include \"%s\"", 321270096Strasz n->n_key); 322270096Strasz } 323270096Strasz 324270096Strasz /* 325270096Strasz * Entries to be included are now in tmproot. We need to merge 326270096Strasz * them with the rest, preserving their place and ordering. 327270096Strasz */ 328270096Strasz TAILQ_FOREACH_REVERSE_SAFE(n2, 329270096Strasz &tmproot->n_children, nodehead, n_next, tmp2) { 330270096Strasz node_move_after(n2, n); 331270096Strasz } 332270096Strasz 333270096Strasz node_delete(n); 334270096Strasz node_delete(tmproot); 335270096Strasz } 336270096Strasz} 337270096Strasz 338270096Straszstatic char * 339270096Straszexpand_ampersand(char *string, const char *key) 340270096Strasz{ 341270096Strasz char c, *expanded; 342270096Strasz int i, ret, before_len = 0; 343270096Strasz bool backslashed = false; 344270096Strasz 345270096Strasz assert(key[0] != '\0'); 346270096Strasz 347270096Strasz expanded = checked_strdup(string); 348270096Strasz 349270096Strasz for (i = 0; string[i] != '\0'; i++) { 350270096Strasz c = string[i]; 351270096Strasz if (c == '\\' && backslashed == false) { 352270096Strasz backslashed = true; 353270096Strasz continue; 354270096Strasz } 355270096Strasz if (backslashed) { 356270096Strasz backslashed = false; 357270096Strasz continue; 358270096Strasz } 359270096Strasz backslashed = false; 360270096Strasz if (c != '&') 361270096Strasz continue; 362270096Strasz 363270096Strasz /* 364270096Strasz * The 'before_len' variable contains the number 365270096Strasz * of characters before the '&'. 366270096Strasz */ 367270096Strasz before_len = i; 368270096Strasz //assert(i + 1 < (int)strlen(string)); 369270096Strasz 370270096Strasz ret = asprintf(&expanded, "%.*s%s%s", 371270096Strasz before_len, string, key, string + before_len + 1); 372270096Strasz if (ret < 0) 373270096Strasz log_err(1, "asprintf"); 374270096Strasz 375270096Strasz //log_debugx("\"%s\" expanded with key \"%s\" to \"%s\"", 376270096Strasz // string, key, expanded); 377270096Strasz 378270096Strasz /* 379270096Strasz * Figure out where to start searching for next variable. 380270096Strasz */ 381270096Strasz string = expanded; 382270096Strasz i = before_len + strlen(key); 383270096Strasz backslashed = false; 384270096Strasz //assert(i < (int)strlen(string)); 385270096Strasz } 386270096Strasz 387270096Strasz return (expanded); 388270096Strasz} 389270096Strasz 390270096Strasz/* 391270096Strasz * Expand "&" in n_location. If the key is NULL, try to use 392270096Strasz * key from map entries themselves. Keep in mind that maps 393270096Strasz * consist of tho levels of node structures, the key is one 394270096Strasz * level up. 395270096Strasz * 396270096Strasz * Variant with NULL key is for "automount -LL". 397270096Strasz */ 398270096Straszvoid 399270096Strasznode_expand_ampersand(struct node *n, const char *key) 400270096Strasz{ 401270096Strasz struct node *child; 402270096Strasz 403270096Strasz if (n->n_location != NULL) { 404270096Strasz if (key == NULL) { 405270096Strasz if (n->n_parent != NULL && 406270096Strasz strcmp(n->n_parent->n_key, "*") != 0) { 407270096Strasz n->n_location = expand_ampersand(n->n_location, 408270096Strasz n->n_parent->n_key); 409270096Strasz } 410270096Strasz } else { 411270096Strasz n->n_location = expand_ampersand(n->n_location, key); 412270096Strasz } 413270096Strasz } 414270096Strasz 415270096Strasz TAILQ_FOREACH(child, &n->n_children, n_next) 416270096Strasz node_expand_ampersand(child, key); 417270096Strasz} 418270096Strasz 419270096Strasz/* 420270096Strasz * Expand "*" in n_key. 421270096Strasz */ 422270096Straszvoid 423270096Strasznode_expand_wildcard(struct node *n, const char *key) 424270096Strasz{ 425270096Strasz struct node *child, *expanded; 426270096Strasz 427270096Strasz assert(key != NULL); 428270096Strasz 429270096Strasz if (strcmp(n->n_key, "*") == 0) { 430270096Strasz expanded = node_duplicate(n, NULL); 431270096Strasz expanded->n_key = checked_strdup(key); 432270096Strasz node_move_after(expanded, n); 433270096Strasz } 434270096Strasz 435270096Strasz TAILQ_FOREACH(child, &n->n_children, n_next) 436270096Strasz node_expand_wildcard(child, key); 437270096Strasz} 438270096Strasz 439270096Straszint 440270096Strasznode_expand_defined(struct node *n) 441270096Strasz{ 442270096Strasz struct node *child; 443270096Strasz int error, cumulated_error = 0; 444270096Strasz 445270096Strasz if (n->n_location != NULL) { 446270096Strasz n->n_location = defined_expand(n->n_location); 447270096Strasz if (n->n_location == NULL) { 448270096Strasz log_warnx("failed to expand location for %s", 449270096Strasz node_path(n)); 450270096Strasz return (EINVAL); 451270096Strasz } 452270096Strasz } 453270096Strasz 454270096Strasz TAILQ_FOREACH(child, &n->n_children, n_next) { 455270096Strasz error = node_expand_defined(child); 456270096Strasz if (error != 0 && cumulated_error == 0) 457270096Strasz cumulated_error = error; 458270096Strasz } 459270096Strasz 460270096Strasz return (cumulated_error); 461270096Strasz} 462270096Strasz 463283232Straszstatic bool 464283232Strasznode_is_direct_key(const struct node *n) 465283232Strasz{ 466283232Strasz 467283232Strasz if (n->n_parent != NULL && n->n_parent->n_parent == NULL && 468283232Strasz strcmp(n->n_key, "/-") == 0) { 469283232Strasz return (true); 470283232Strasz } 471283232Strasz 472283232Strasz return (false); 473283232Strasz} 474283232Strasz 475270096Straszbool 476270096Strasznode_is_direct_map(const struct node *n) 477270096Strasz{ 478270096Strasz 479270096Strasz for (;;) { 480270096Strasz assert(n->n_parent != NULL); 481270096Strasz if (n->n_parent->n_parent == NULL) 482270096Strasz break; 483270096Strasz n = n->n_parent; 484270096Strasz } 485270096Strasz 486283232Strasz return (node_is_direct_key(n)); 487270096Strasz} 488270096Strasz 489279741Straszbool 490279741Strasznode_has_wildcards(const struct node *n) 491279741Strasz{ 492279741Strasz const struct node *child; 493279741Strasz 494279741Strasz TAILQ_FOREACH(child, &n->n_children, n_next) { 495279741Strasz if (strcmp(child->n_key, "*") == 0) 496279741Strasz return (true); 497279741Strasz } 498279741Strasz 499279741Strasz return (false); 500279741Strasz} 501279741Strasz 502270096Straszstatic void 503270096Strasznode_expand_maps(struct node *n, bool indirect) 504270096Strasz{ 505270096Strasz struct node *child, *tmp; 506270096Strasz 507270096Strasz TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) { 508270096Strasz if (node_is_direct_map(child)) { 509270096Strasz if (indirect) 510270096Strasz continue; 511270096Strasz } else { 512270096Strasz if (indirect == false) 513270096Strasz continue; 514270096Strasz } 515270096Strasz 516270096Strasz /* 517270096Strasz * This is the first-level map node; the one that contains 518270096Strasz * the key and subnodes with mountpoints and actual map names. 519270096Strasz */ 520270096Strasz if (child->n_map == NULL) 521270096Strasz continue; 522270096Strasz 523270096Strasz if (indirect) { 524270096Strasz log_debugx("map \"%s\" is an indirect map, parsing", 525270096Strasz child->n_map); 526270096Strasz } else { 527270096Strasz log_debugx("map \"%s\" is a direct map, parsing", 528270096Strasz child->n_map); 529270096Strasz } 530279741Strasz parse_map(child, child->n_map, NULL, NULL); 531270096Strasz } 532270096Strasz} 533270096Strasz 534270096Straszstatic void 535270096Strasznode_expand_direct_maps(struct node *n) 536270096Strasz{ 537270096Strasz 538270096Strasz node_expand_maps(n, false); 539270096Strasz} 540270096Strasz 541270096Straszvoid 542270096Strasznode_expand_indirect_maps(struct node *n) 543270096Strasz{ 544270096Strasz 545270096Strasz node_expand_maps(n, true); 546270096Strasz} 547270096Strasz 548270096Straszstatic char * 549270096Strasznode_path_x(const struct node *n, char *x) 550270096Strasz{ 551270096Strasz char *path; 552270096Strasz 553270096Strasz if (n->n_parent == NULL) 554270096Strasz return (x); 555270096Strasz 556270096Strasz /* 557270096Strasz * Return "/-" for direct maps only if we were asked for path 558270096Strasz * to the "/-" node itself, not to any of its subnodes. 559270096Strasz */ 560283232Strasz if (node_is_direct_key(n) && x[0] != '\0') 561270096Strasz return (x); 562270096Strasz 563270903Strasz assert(n->n_key[0] != '\0'); 564283231Strasz path = concat(n->n_key, '/', x); 565270096Strasz free(x); 566270096Strasz 567270096Strasz return (node_path_x(n->n_parent, path)); 568270096Strasz} 569270096Strasz 570270096Strasz/* 571270096Strasz * Return full path for node, consisting of concatenated 572270096Strasz * paths of node itself and all its parents, up to the root. 573270096Strasz */ 574270096Straszchar * 575270096Strasznode_path(const struct node *n) 576270096Strasz{ 577283227Strasz char *path; 578283227Strasz size_t len; 579270096Strasz 580283227Strasz path = node_path_x(n, checked_strdup("")); 581283227Strasz 582283227Strasz /* 583283227Strasz * Strip trailing slash, unless the whole path is "/". 584283227Strasz */ 585283227Strasz len = strlen(path); 586283227Strasz if (len > 1 && path[len - 1] == '/') 587283227Strasz path[len - 1] = '\0'; 588283227Strasz 589283227Strasz return (path); 590270096Strasz} 591270096Strasz 592270096Straszstatic char * 593270096Strasznode_options_x(const struct node *n, char *x) 594270096Strasz{ 595270096Strasz char *options; 596270096Strasz 597283229Strasz if (n == NULL) 598283229Strasz return (x); 599283229Strasz 600283231Strasz options = concat(x, ',', n->n_options); 601283229Strasz free(x); 602270096Strasz 603270096Strasz return (node_options_x(n->n_parent, options)); 604270096Strasz} 605270096Strasz 606270096Strasz/* 607270096Strasz * Return options for node, consisting of concatenated 608270096Strasz * options from the node itself and all its parents, 609270096Strasz * up to the root. 610270096Strasz */ 611270096Straszchar * 612270096Strasznode_options(const struct node *n) 613270096Strasz{ 614270096Strasz 615270096Strasz return (node_options_x(n, checked_strdup(""))); 616270096Strasz} 617270096Strasz 618270096Straszstatic void 619283239Strasznode_print_indent(const struct node *n, const char *cmdline_options, 620283239Strasz int indent) 621270096Strasz{ 622270096Strasz const struct node *child, *first_child; 623283239Strasz char *path, *options, *tmp; 624270096Strasz 625270096Strasz path = node_path(n); 626283239Strasz tmp = node_options(n); 627283239Strasz options = concat(cmdline_options, ',', tmp); 628283239Strasz free(tmp); 629270096Strasz 630270096Strasz /* 631270096Strasz * Do not show both parent and child node if they have the same 632270096Strasz * mountpoint; only show the child node. This means the typical, 633270096Strasz * "key location", map entries are shown in a single line; 634270096Strasz * the "key mountpoint1 location2 mountpoint2 location2" entries 635270096Strasz * take multiple lines. 636270096Strasz */ 637270096Strasz first_child = TAILQ_FIRST(&n->n_children); 638270096Strasz if (first_child == NULL || TAILQ_NEXT(first_child, n_next) != NULL || 639270096Strasz strcmp(path, node_path(first_child)) != 0) { 640270096Strasz assert(n->n_location == NULL || n->n_map == NULL); 641270096Strasz printf("%*.s%-*s %s%-*s %-*s # %s map %s at %s:%d\n", 642270096Strasz indent, "", 643270096Strasz 25 - indent, 644270096Strasz path, 645270096Strasz options[0] != '\0' ? "-" : " ", 646270096Strasz 20, 647270096Strasz options[0] != '\0' ? options : "", 648270096Strasz 20, 649270096Strasz n->n_location != NULL ? n->n_location : n->n_map != NULL ? n->n_map : "", 650270096Strasz node_is_direct_map(n) ? "direct" : "indirect", 651270096Strasz indent == 0 ? "referenced" : "defined", 652270096Strasz n->n_config_file, n->n_config_line); 653270096Strasz } 654270096Strasz 655270096Strasz free(path); 656270096Strasz free(options); 657270096Strasz 658270096Strasz TAILQ_FOREACH(child, &n->n_children, n_next) 659283239Strasz node_print_indent(child, cmdline_options, indent + 2); 660270096Strasz} 661270096Strasz 662283239Strasz/* 663283239Strasz * Recursively print node with all its children. The cmdline_options 664283239Strasz * argument is used for additional options to be prepended to all the 665283239Strasz * others - usually those are the options passed by command line. 666283239Strasz */ 667270096Straszvoid 668283239Strasznode_print(const struct node *n, const char *cmdline_options) 669270096Strasz{ 670270096Strasz const struct node *child; 671270096Strasz 672270096Strasz TAILQ_FOREACH(child, &n->n_children, n_next) 673283239Strasz node_print_indent(child, cmdline_options, 0); 674270096Strasz} 675270096Strasz 676279744Straszstatic struct node * 677279744Strasznode_find_x(struct node *node, const char *path) 678270096Strasz{ 679270096Strasz struct node *child, *found; 680270096Strasz char *tmp; 681272117Strasz size_t tmplen; 682270096Strasz 683283233Strasz //log_debugx("looking up %s in %s", path, node_path(node)); 684270096Strasz 685283233Strasz if (!node_is_direct_key(node)) { 686283233Strasz tmp = node_path(node); 687283233Strasz tmplen = strlen(tmp); 688283233Strasz if (strncmp(tmp, path, tmplen) != 0) { 689283233Strasz free(tmp); 690283233Strasz return (NULL); 691283233Strasz } 692283233Strasz if (path[tmplen] != '/' && path[tmplen] != '\0') { 693283233Strasz /* 694283233Strasz * If we have two map entries like 'foo' and 'foobar', make 695283233Strasz * sure the search for 'foobar' won't match 'foo' instead. 696283233Strasz */ 697283233Strasz free(tmp); 698283233Strasz return (NULL); 699283233Strasz } 700270096Strasz free(tmp); 701270096Strasz } 702270096Strasz 703270096Strasz TAILQ_FOREACH(child, &node->n_children, n_next) { 704279744Strasz found = node_find_x(child, path); 705270096Strasz if (found != NULL) 706270096Strasz return (found); 707270096Strasz } 708270096Strasz 709283233Strasz if (node->n_parent == NULL || node_is_direct_key(node)) 710283233Strasz return (NULL); 711283233Strasz 712270096Strasz return (node); 713270096Strasz} 714270096Strasz 715279744Straszstruct node * 716279744Strasznode_find(struct node *root, const char *path) 717279744Strasz{ 718279744Strasz struct node *node; 719279744Strasz 720283233Strasz assert(root->n_parent == NULL); 721283233Strasz 722279744Strasz node = node_find_x(root, path); 723283233Strasz if (node != NULL) 724283233Strasz assert(node != root); 725283233Strasz 726279744Strasz return (node); 727279744Strasz} 728279744Strasz 729270096Strasz/* 730270096Strasz * Canonical form of a map entry looks like this: 731270096Strasz * 732270096Strasz * key [-options] [ [/mountpoint] [-options2] location ... ] 733270096Strasz * 734270096Strasz * Entries for executable maps are slightly different, as they 735270096Strasz * lack the 'key' field and are always single-line; the key field 736270096Strasz * for those maps is taken from 'executable_key' argument. 737270096Strasz * 738270096Strasz * We parse it in such a way that a map always has two levels - first 739270096Strasz * for key, and the second, for the mountpoint. 740270096Strasz */ 741270096Straszstatic void 742270096Straszparse_map_yyin(struct node *parent, const char *map, const char *executable_key) 743270096Strasz{ 744270096Strasz char *key = NULL, *options = NULL, *mountpoint = NULL, 745270096Strasz *options2 = NULL, *location = NULL; 746270096Strasz int ret; 747270096Strasz struct node *node; 748270096Strasz 749270096Strasz lineno = 1; 750270096Strasz 751270096Strasz if (executable_key != NULL) 752270096Strasz key = checked_strdup(executable_key); 753270096Strasz 754270096Strasz for (;;) { 755270096Strasz ret = yylex(); 756270096Strasz if (ret == 0 || ret == NEWLINE) { 757270901Strasz /* 758270901Strasz * In case of executable map, the key is always 759270901Strasz * non-NULL, even if the map is empty. So, make sure 760270901Strasz * we don't fail empty maps here. 761270901Strasz */ 762270901Strasz if ((key != NULL && executable_key == NULL) || 763270901Strasz options != NULL) { 764270096Strasz log_errx(1, "truncated entry at %s, line %d", 765270096Strasz map, lineno); 766270096Strasz } 767270096Strasz if (ret == 0 || executable_key != NULL) { 768270096Strasz /* 769270096Strasz * End of file. 770270096Strasz */ 771270096Strasz break; 772270096Strasz } else { 773270096Strasz key = options = NULL; 774270096Strasz continue; 775270096Strasz } 776270096Strasz } 777270096Strasz if (key == NULL) { 778270096Strasz key = checked_strdup(yytext); 779270096Strasz if (key[0] == '+') { 780270096Strasz node_new(parent, key, NULL, NULL, map, lineno); 781270096Strasz key = options = NULL; 782270096Strasz continue; 783270096Strasz } 784270096Strasz continue; 785270096Strasz } else if (yytext[0] == '-') { 786270096Strasz if (options != NULL) { 787270096Strasz log_errx(1, "duplicated options at %s, line %d", 788270096Strasz map, lineno); 789270096Strasz } 790270096Strasz /* 791270096Strasz * +1 to skip leading "-". 792270096Strasz */ 793270096Strasz options = checked_strdup(yytext + 1); 794270096Strasz continue; 795270096Strasz } 796270096Strasz 797270096Strasz /* 798270096Strasz * We cannot properly handle a situation where the map key 799270096Strasz * is "/". Ignore such entries. 800270096Strasz * 801270096Strasz * XXX: According to Piete Brooks, Linux automounter uses 802270096Strasz * "/" as a wildcard character in LDAP maps. Perhaps 803270096Strasz * we should work around this braindamage by substituting 804270096Strasz * "*" for "/"? 805270096Strasz */ 806270096Strasz if (strcmp(key, "/") == 0) { 807270096Strasz log_warnx("nonsensical map key \"/\" at %s, line %d; " 808270096Strasz "ignoring map entry ", map, lineno); 809270096Strasz 810270096Strasz /* 811270096Strasz * Skip the rest of the entry. 812270096Strasz */ 813270096Strasz do { 814270096Strasz ret = yylex(); 815270096Strasz } while (ret != 0 && ret != NEWLINE); 816270096Strasz 817270096Strasz key = options = NULL; 818270096Strasz continue; 819270096Strasz } 820270096Strasz 821270096Strasz //log_debugx("adding map node, %s", key); 822270096Strasz node = node_new(parent, key, options, NULL, map, lineno); 823270096Strasz key = options = NULL; 824270096Strasz 825270096Strasz for (;;) { 826270096Strasz if (yytext[0] == '/') { 827270096Strasz if (mountpoint != NULL) { 828270096Strasz log_errx(1, "duplicated mountpoint " 829270096Strasz "in %s, line %d", map, lineno); 830270096Strasz } 831270096Strasz if (options2 != NULL || location != NULL) { 832270096Strasz log_errx(1, "mountpoint out of order " 833270096Strasz "in %s, line %d", map, lineno); 834270096Strasz } 835270096Strasz mountpoint = checked_strdup(yytext); 836270096Strasz goto again; 837270096Strasz } 838270096Strasz 839270096Strasz if (yytext[0] == '-') { 840270096Strasz if (options2 != NULL) { 841270096Strasz log_errx(1, "duplicated options " 842270096Strasz "in %s, line %d", map, lineno); 843270096Strasz } 844270096Strasz if (location != NULL) { 845270096Strasz log_errx(1, "options out of order " 846270096Strasz "in %s, line %d", map, lineno); 847270096Strasz } 848270096Strasz options2 = checked_strdup(yytext + 1); 849270096Strasz goto again; 850270096Strasz } 851270096Strasz 852270096Strasz if (location != NULL) { 853270096Strasz log_errx(1, "too many arguments " 854270096Strasz "in %s, line %d", map, lineno); 855270096Strasz } 856270096Strasz 857270096Strasz /* 858270096Strasz * If location field starts with colon, e.g. ":/dev/cd0", 859270096Strasz * then strip it. 860270096Strasz */ 861270096Strasz if (yytext[0] == ':') { 862270096Strasz location = checked_strdup(yytext + 1); 863270096Strasz if (location[0] == '\0') { 864270096Strasz log_errx(1, "empty location in %s, " 865270096Strasz "line %d", map, lineno); 866270096Strasz } 867270096Strasz } else { 868270096Strasz location = checked_strdup(yytext); 869270096Strasz } 870270096Strasz 871270096Strasz if (mountpoint == NULL) 872270096Strasz mountpoint = checked_strdup("/"); 873270096Strasz if (options2 == NULL) 874270096Strasz options2 = checked_strdup(""); 875270096Strasz 876270096Strasz#if 0 877270096Strasz log_debugx("adding map node, %s %s %s", 878270096Strasz mountpoint, options2, location); 879270096Strasz#endif 880270096Strasz node_new(node, mountpoint, options2, location, 881270096Strasz map, lineno); 882270096Strasz mountpoint = options2 = location = NULL; 883270096Straszagain: 884270096Strasz ret = yylex(); 885270096Strasz if (ret == 0 || ret == NEWLINE) { 886270096Strasz if (mountpoint != NULL || options2 != NULL || 887270096Strasz location != NULL) { 888270096Strasz log_errx(1, "truncated entry " 889270096Strasz "in %s, line %d", map, lineno); 890270096Strasz } 891270096Strasz break; 892270096Strasz } 893270096Strasz } 894270096Strasz } 895270096Strasz} 896270096Strasz 897270902Strasz/* 898270903Strasz * Parse output of a special map called without argument. It is a list 899270903Strasz * of keys, separated by newlines. They can contain whitespace, so use 900270903Strasz * getline(3) instead of lexer used for maps. 901270902Strasz */ 902270902Straszstatic void 903270902Straszparse_map_keys_yyin(struct node *parent, const char *map) 904270902Strasz{ 905270903Strasz char *line = NULL, *key; 906270903Strasz size_t linecap = 0; 907270903Strasz ssize_t linelen; 908270902Strasz 909270902Strasz lineno = 1; 910270902Strasz 911270902Strasz for (;;) { 912270903Strasz linelen = getline(&line, &linecap, yyin); 913270903Strasz if (linelen < 0) { 914270902Strasz /* 915270902Strasz * End of file. 916270902Strasz */ 917270902Strasz break; 918270902Strasz } 919270903Strasz if (linelen <= 1) { 920270903Strasz /* 921270903Strasz * Empty line, consisting of just the newline. 922270903Strasz */ 923270903Strasz continue; 924270903Strasz } 925270902Strasz 926270903Strasz /* 927270903Strasz * "-1" to strip the trailing newline. 928270903Strasz */ 929270903Strasz key = strndup(line, linelen - 1); 930270903Strasz 931270903Strasz log_debugx("adding key \"%s\"", key); 932270902Strasz node_new(parent, key, NULL, NULL, map, lineno); 933270903Strasz lineno++; 934270902Strasz } 935270903Strasz free(line); 936270902Strasz} 937270902Strasz 938270096Straszstatic bool 939270096Straszfile_is_executable(const char *path) 940270096Strasz{ 941270096Strasz struct stat sb; 942270096Strasz int error; 943270096Strasz 944270096Strasz error = stat(path, &sb); 945270096Strasz if (error != 0) 946270096Strasz log_err(1, "cannot stat %s", path); 947270096Strasz if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) || 948270096Strasz (sb.st_mode & S_IXOTH)) 949270096Strasz return (true); 950270096Strasz return (false); 951270096Strasz} 952270096Strasz 953270096Strasz/* 954270096Strasz * Parse a special map, e.g. "-hosts". 955270096Strasz */ 956270096Straszstatic void 957270096Straszparse_special_map(struct node *parent, const char *map, const char *key) 958270096Strasz{ 959270096Strasz char *path; 960270096Strasz int error, ret; 961270096Strasz 962270096Strasz assert(map[0] == '-'); 963270096Strasz 964270096Strasz /* 965270096Strasz * +1 to skip leading "-" in map name. 966270096Strasz */ 967270096Strasz ret = asprintf(&path, "%s/special_%s", AUTO_SPECIAL_PREFIX, map + 1); 968270096Strasz if (ret < 0) 969270096Strasz log_err(1, "asprintf"); 970270096Strasz 971270096Strasz yyin = auto_popen(path, key, NULL); 972270096Strasz assert(yyin != NULL); 973270096Strasz 974270902Strasz if (key == NULL) { 975270902Strasz parse_map_keys_yyin(parent, map); 976270902Strasz } else { 977270902Strasz parse_map_yyin(parent, map, key); 978270902Strasz } 979270096Strasz 980270096Strasz error = auto_pclose(yyin); 981270096Strasz yyin = NULL; 982270096Strasz if (error != 0) 983270096Strasz log_errx(1, "failed to handle special map \"%s\"", map); 984270096Strasz 985270096Strasz node_expand_includes(parent, false); 986270096Strasz node_expand_direct_maps(parent); 987270096Strasz 988270096Strasz free(path); 989270096Strasz} 990270096Strasz 991270096Strasz/* 992270096Strasz * Retrieve and parse map from directory services, e.g. LDAP. 993270096Strasz * Note that it is different from executable maps, in that 994270096Strasz * the include script outputs the whole map to standard output 995270096Strasz * (as opposed to executable maps that only output a single 996270096Strasz * entry, without the key), and it takes the map name as an 997270096Strasz * argument, instead of key. 998270096Strasz */ 999270096Straszstatic void 1000270096Straszparse_included_map(struct node *parent, const char *map) 1001270096Strasz{ 1002270096Strasz int error; 1003270096Strasz 1004270096Strasz assert(map[0] != '-'); 1005270096Strasz assert(map[0] != '/'); 1006270096Strasz 1007270096Strasz error = access(AUTO_INCLUDE_PATH, F_OK); 1008270096Strasz if (error != 0) { 1009270096Strasz log_errx(1, "directory services not configured;" 1010270096Strasz " %s does not exist", AUTO_INCLUDE_PATH); 1011270096Strasz } 1012270096Strasz 1013270096Strasz yyin = auto_popen(AUTO_INCLUDE_PATH, map, NULL); 1014270096Strasz assert(yyin != NULL); 1015270096Strasz 1016270096Strasz parse_map_yyin(parent, map, NULL); 1017270096Strasz 1018270096Strasz error = auto_pclose(yyin); 1019270096Strasz yyin = NULL; 1020270096Strasz if (error != 0) 1021270096Strasz log_errx(1, "failed to handle remote map \"%s\"", map); 1022270096Strasz 1023270096Strasz node_expand_includes(parent, false); 1024270096Strasz node_expand_direct_maps(parent); 1025270096Strasz} 1026270096Strasz 1027270096Straszvoid 1028279741Straszparse_map(struct node *parent, const char *map, const char *key, 1029279741Strasz bool *wildcards) 1030270096Strasz{ 1031270096Strasz char *path = NULL; 1032270096Strasz int error, ret; 1033270096Strasz bool executable; 1034270096Strasz 1035270096Strasz assert(map != NULL); 1036270096Strasz assert(map[0] != '\0'); 1037270096Strasz 1038270096Strasz log_debugx("parsing map \"%s\"", map); 1039270096Strasz 1040279741Strasz if (wildcards != NULL) 1041279741Strasz *wildcards = false; 1042279741Strasz 1043279741Strasz if (map[0] == '-') { 1044279741Strasz if (wildcards != NULL) 1045279741Strasz *wildcards = true; 1046270096Strasz return (parse_special_map(parent, map, key)); 1047279741Strasz } 1048270096Strasz 1049270096Strasz if (map[0] == '/') { 1050270096Strasz path = checked_strdup(map); 1051270096Strasz } else { 1052270096Strasz ret = asprintf(&path, "%s/%s", AUTO_MAP_PREFIX, map); 1053270096Strasz if (ret < 0) 1054270096Strasz log_err(1, "asprintf"); 1055270096Strasz log_debugx("map \"%s\" maps to \"%s\"", map, path); 1056270096Strasz 1057270096Strasz /* 1058270096Strasz * See if the file exists. If not, try to obtain the map 1059270096Strasz * from directory services. 1060270096Strasz */ 1061270096Strasz error = access(path, F_OK); 1062270096Strasz if (error != 0) { 1063270096Strasz log_debugx("map file \"%s\" does not exist; falling " 1064270096Strasz "back to directory services", path); 1065270096Strasz return (parse_included_map(parent, map)); 1066270096Strasz } 1067270096Strasz } 1068270096Strasz 1069270096Strasz executable = file_is_executable(path); 1070270096Strasz 1071270096Strasz if (executable) { 1072270096Strasz log_debugx("map \"%s\" is executable", map); 1073270096Strasz 1074279741Strasz if (wildcards != NULL) 1075279741Strasz *wildcards = true; 1076279741Strasz 1077270096Strasz if (key != NULL) { 1078270096Strasz yyin = auto_popen(path, key, NULL); 1079270096Strasz } else { 1080270096Strasz yyin = auto_popen(path, NULL); 1081270096Strasz } 1082270096Strasz assert(yyin != NULL); 1083270096Strasz } else { 1084270096Strasz yyin = fopen(path, "r"); 1085270096Strasz if (yyin == NULL) 1086270096Strasz log_err(1, "unable to open \"%s\"", path); 1087270096Strasz } 1088270096Strasz 1089270096Strasz free(path); 1090270096Strasz path = NULL; 1091270096Strasz 1092270096Strasz parse_map_yyin(parent, map, executable ? key : NULL); 1093270096Strasz 1094270096Strasz if (executable) { 1095270096Strasz error = auto_pclose(yyin); 1096270096Strasz yyin = NULL; 1097270096Strasz if (error != 0) { 1098270096Strasz log_errx(1, "failed to handle executable map \"%s\"", 1099270096Strasz map); 1100270096Strasz } 1101270096Strasz } else { 1102270096Strasz fclose(yyin); 1103270096Strasz } 1104270096Strasz yyin = NULL; 1105270096Strasz 1106270096Strasz log_debugx("done parsing map \"%s\"", map); 1107270096Strasz 1108270096Strasz node_expand_includes(parent, false); 1109270096Strasz node_expand_direct_maps(parent); 1110270096Strasz} 1111270096Strasz 1112270096Straszstatic void 1113270096Straszparse_master_yyin(struct node *root, const char *master) 1114270096Strasz{ 1115270096Strasz char *mountpoint = NULL, *map = NULL, *options = NULL; 1116270096Strasz int ret; 1117270096Strasz 1118270096Strasz /* 1119270096Strasz * XXX: 1 gives incorrect values; wtf? 1120270096Strasz */ 1121270096Strasz lineno = 0; 1122270096Strasz 1123270096Strasz for (;;) { 1124270096Strasz ret = yylex(); 1125270096Strasz if (ret == 0 || ret == NEWLINE) { 1126270096Strasz if (mountpoint != NULL) { 1127270096Strasz //log_debugx("adding map for %s", mountpoint); 1128270096Strasz node_new_map(root, mountpoint, options, map, 1129270096Strasz master, lineno); 1130270096Strasz } 1131270096Strasz if (ret == 0) { 1132270096Strasz break; 1133270096Strasz } else { 1134270096Strasz mountpoint = map = options = NULL; 1135270096Strasz continue; 1136270096Strasz } 1137270096Strasz } 1138270096Strasz if (mountpoint == NULL) { 1139270096Strasz mountpoint = checked_strdup(yytext); 1140270096Strasz } else if (map == NULL) { 1141270096Strasz map = checked_strdup(yytext); 1142270096Strasz } else if (options == NULL) { 1143270096Strasz /* 1144270096Strasz * +1 to skip leading "-". 1145270096Strasz */ 1146270096Strasz options = checked_strdup(yytext + 1); 1147270096Strasz } else { 1148270096Strasz log_errx(1, "too many arguments at %s, line %d", 1149270096Strasz master, lineno); 1150270096Strasz } 1151270096Strasz } 1152270096Strasz} 1153270096Strasz 1154270096Straszvoid 1155270096Straszparse_master(struct node *root, const char *master) 1156270096Strasz{ 1157270096Strasz 1158270096Strasz log_debugx("parsing auto_master file at \"%s\"", master); 1159270096Strasz 1160270096Strasz yyin = fopen(master, "r"); 1161270096Strasz if (yyin == NULL) 1162270096Strasz err(1, "unable to open %s", master); 1163270096Strasz 1164270096Strasz parse_master_yyin(root, master); 1165270096Strasz 1166270096Strasz fclose(yyin); 1167270096Strasz yyin = NULL; 1168270096Strasz 1169270096Strasz log_debugx("done parsing \"%s\"", master); 1170270096Strasz 1171270096Strasz node_expand_includes(root, true); 1172270096Strasz node_expand_direct_maps(root); 1173270096Strasz} 1174270096Strasz 1175270096Strasz/* 1176270096Strasz * Two things daemon(3) does, that we actually also want to do 1177270096Strasz * when running in foreground, is closing the stdin and chdiring 1178270096Strasz * to "/". This is what we do here. 1179270096Strasz */ 1180270096Straszvoid 1181270096Straszlesser_daemon(void) 1182270096Strasz{ 1183270096Strasz int error, fd; 1184270096Strasz 1185270096Strasz error = chdir("/"); 1186270096Strasz if (error != 0) 1187270096Strasz log_warn("chdir"); 1188270096Strasz 1189270096Strasz fd = open(_PATH_DEVNULL, O_RDWR, 0); 1190270096Strasz if (fd < 0) { 1191270096Strasz log_warn("cannot open %s", _PATH_DEVNULL); 1192270096Strasz return; 1193270096Strasz } 1194270096Strasz 1195270096Strasz error = dup2(fd, STDIN_FILENO); 1196270096Strasz if (error != 0) 1197270096Strasz log_warn("dup2"); 1198270096Strasz 1199270096Strasz error = close(fd); 1200270096Strasz if (error != 0) { 1201270096Strasz /* Bloody hell. */ 1202270096Strasz log_warn("close"); 1203270096Strasz } 1204270096Strasz} 1205270096Strasz 1206270096Straszint 1207270096Straszmain(int argc, char **argv) 1208270096Strasz{ 1209270096Strasz char *cmdname; 1210270096Strasz 1211270096Strasz if (argv[0] == NULL) 1212270096Strasz log_errx(1, "NULL command name"); 1213270096Strasz 1214270096Strasz cmdname = basename(argv[0]); 1215270096Strasz 1216270096Strasz if (strcmp(cmdname, "automount") == 0) 1217270096Strasz return (main_automount(argc, argv)); 1218270096Strasz else if (strcmp(cmdname, "automountd") == 0) 1219270096Strasz return (main_automountd(argc, argv)); 1220270096Strasz else if (strcmp(cmdname, "autounmountd") == 0) 1221270096Strasz return (main_autounmountd(argc, argv)); 1222270096Strasz else 1223270096Strasz log_errx(1, "binary name should be either \"automount\", " 1224270096Strasz "\"automountd\", or \"autounmountd\""); 1225270096Strasz} 1226