amd.c revision 310490
1/* 2 * Copyright (c) 1997-2014 Erez Zadok 3 * Copyright (c) 1989 Jan-Simon Pendry 4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1989 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * 36 * $Id: amd.c,v 1.8.2.6 2004/01/06 03:15:16 ezk Exp $ 37 * File: am-utils/amd/amd.c 38 * 39 */ 40 41#include <sys/cdefs.h> 42__FBSDID("$FreeBSD: stable/10/contrib/amd/amd/amd.c 310490 2016-12-23 22:42:14Z cy $"); 43 44/* 45 * Automounter 46 */ 47 48#ifdef HAVE_CONFIG_H 49# include <config.h> 50#endif /* HAVE_CONFIG_H */ 51#include <am_defs.h> 52#include <amd.h> 53 54struct amu_global_options gopt; /* where global options are stored */ 55 56char pid_fsname[SIZEOF_PID_FSNAME]; /* "kiska.southseas.nz:(pid%d)" */ 57char *hostdomain = "unknown.domain"; 58#define SIZEOF_HOSTD (2 * MAXHOSTNAMELEN + 1) /* Host+domain */ 59char hostd[SIZEOF_HOSTD]; /* Host+domain */ 60char *endian = ARCH_ENDIAN; /* Big or Little endian */ 61char *cpu = HOST_CPU; /* CPU type */ 62char *PrimNetName; /* name of primary network */ 63char *PrimNetNum; /* number of primary network */ 64 65int immediate_abort; /* Should close-down unmounts be retried */ 66int orig_umask = 022; 67int select_intr_valid; 68 69jmp_buf select_intr; 70struct amd_stats amd_stats; /* Server statistics */ 71struct in_addr myipaddr; /* (An) IP address of this host */ 72time_t do_mapc_reload = 0; /* mapc_reload() call required? */ 73 74#ifdef HAVE_FS_AUTOFS 75int amd_use_autofs = 0; 76#endif /* HAVE_FS_AUTOFS */ 77 78#ifdef HAVE_SIGACTION 79sigset_t masked_sigs; 80#endif /* HAVE_SIGACTION */ 81 82 83/* 84 * Signal handler: 85 * SIGINT - tells amd to do a full shutdown, including unmounting all 86 * filesystem. 87 * SIGTERM - tells amd to shutdown now. Just unmounts the automount nodes. 88 */ 89static RETSIGTYPE 90sigterm(int sig) 91{ 92#ifdef REINSTALL_SIGNAL_HANDLER 93 signal(sig, sigterm); 94#endif /* REINSTALL_SIGNAL_HANDLER */ 95 96 switch (sig) { 97 case SIGINT: 98 immediate_abort = 15; 99 break; 100 101 case SIGTERM: 102 immediate_abort = -1; 103 /* fall through... */ 104 105 default: 106 plog(XLOG_WARNING, "WARNING: automounter going down on signal %d", sig); 107 break; 108 } 109 if (select_intr_valid) 110 longjmp(select_intr, sig); 111} 112 113 114/* 115 * Hook for cache reload. 116 * When a SIGHUP arrives it schedules a call to mapc_reload 117 */ 118static RETSIGTYPE 119sighup(int sig) 120{ 121#ifdef REINSTALL_SIGNAL_HANDLER 122 signal(sig, sighup); 123#endif /* REINSTALL_SIGNAL_HANDLER */ 124 125 if (sig != SIGHUP) 126 dlog("spurious call to sighup"); 127 /* 128 * Force a reload by zero'ing the timer 129 */ 130 if (amd_state == Run) 131 do_mapc_reload = 0; 132} 133 134 135static RETSIGTYPE 136parent_exit(int sig) 137{ 138 /* 139 * This signal handler is called during Amd initialization. The parent 140 * forks a child to do all the hard automounting work, and waits for a 141 * SIGQUIT signal from the child. When the parent gets the signal it's 142 * supposed to call this handler and exit(3), thus completing the 143 * daemonizing process. Alas, on some systems, especially Linux 2.4/2.6 144 * with Glibc, exit(3) doesn't always terminate the parent process. 145 * Worse, the parent process now refuses to accept any more SIGQUIT 146 * signals -- they are blocked. What's really annoying is that this 147 * doesn't happen all the time, suggesting a race condition somewhere. 148 * (This happens even if I change the logic to use another signal.) I 149 * traced this to something which exit(3) does in addition to exiting the 150 * process, probably some atexit() stuff or other side-effects related to 151 * signal handling. Either way, since at this stage the parent process 152 * just needs to terminate, I'm simply calling _exit(2). Note also that 153 * the OpenGroup doesn't list exit(3) as a recommended "Base Interface" 154 * but they do list _exit(2) as one. This fix seems to work reliably all 155 * the time. -Erez (2/27/2005) 156 */ 157 _exit(0); 158} 159 160 161static int 162daemon_mode(void) 163{ 164 int bgpid; 165 166#ifdef HAVE_SIGACTION 167 struct sigaction sa, osa; 168 169 memset(&sa, 0, sizeof(sa)); 170 sa.sa_handler = parent_exit; 171 sa.sa_flags = 0; 172 sigemptyset(&(sa.sa_mask)); 173 sigaddset(&(sa.sa_mask), SIGQUIT); 174 sigaction(SIGQUIT, &sa, &osa); 175#else /* not HAVE_SIGACTION */ 176 signal(SIGQUIT, parent_exit); 177#endif /* not HAVE_SIGACTION */ 178 179 bgpid = background(); 180 181 if (bgpid != 0) { 182 /* 183 * Now wait for the automount points to 184 * complete. 185 */ 186 for (;;) 187 pause(); 188 /* should never reach here */ 189 } 190#ifdef HAVE_SIGACTION 191 sigaction(SIGQUIT, &osa, NULL); 192#else /* not HAVE_SIGACTION */ 193 signal(SIGQUIT, SIG_DFL); 194#endif /* not HAVE_SIGACTION */ 195 196 /* 197 * Record our pid to make it easier to kill the correct amd. 198 */ 199 if (gopt.flags & CFM_PRINT_PID) { 200 if (STREQ(gopt.pid_file, "/dev/stdout")) { 201 printf("%ld\n", (long) am_mypid); 202 /* flush stdout, just in case */ 203 fflush(stdout); 204 } else { 205 FILE *f; 206 mode_t prev_umask = umask(0022); /* set secure temporary umask */ 207 208 f = fopen(gopt.pid_file, "w"); 209 if (f) { 210 fprintf(f, "%ld\n", (long) am_mypid); 211 (void) fclose(f); 212 } else { 213 fprintf(stderr, "cannot open %s (errno=%d)\n", gopt.pid_file, errno); 214 } 215 umask(prev_umask); /* restore umask */ 216 } 217 } 218 219 /* 220 * Pretend we are in the foreground again 221 */ 222 foreground = 1; 223 224 /* 225 * Dissociate from the controlling terminal 226 */ 227 amu_release_controlling_tty(); 228 229 return getppid(); 230} 231 232 233/* 234 * Initialize global options structure. 235 */ 236static void 237init_global_options(void) 238{ 239#if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) 240 static struct utsname un; 241#endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */ 242 int i; 243 244 memset(&gopt, 0, sizeof(struct amu_global_options)); 245 246 /* name of current architecture */ 247 gopt.arch = HOST_ARCH; 248 249 /* automounter temp dir */ 250 gopt.auto_dir = "/.amd_mnt"; 251 252 /* toplevel attribute cache timeout */ 253 gopt.auto_attrcache = 0; 254 255 /* cluster name */ 256 gopt.cluster = NULL; 257 258 /* executable map timeout */ 259 gopt.exec_map_timeout = AMFS_EXEC_MAP_TIMEOUT; 260 261 /* 262 * kernel architecture: this you must get from uname() if possible. 263 */ 264#if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) 265 if (uname(&un) >= 0) 266 gopt.karch = un.machine; 267 else 268#endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */ 269 gopt.karch = HOST_ARCH; 270 271 /* amd log file */ 272 gopt.logfile = NULL; 273 274 /* operating system name */ 275 gopt.op_sys = HOST_OS_NAME; 276 277 /* OS version */ 278 gopt.op_sys_ver = HOST_OS_VERSION; 279 280 /* full OS name and version */ 281 gopt.op_sys_full = HOST_OS; 282 283 /* OS version */ 284 gopt.op_sys_vendor = HOST_VENDOR; 285 286 /* pid file */ 287 gopt.pid_file = "/dev/stdout"; 288 289 /* local domain */ 290 gopt.sub_domain = NULL; 291 292 /* reset NFS (and toplvl) retransmit counter and retry interval */ 293 for (i=0; i<AMU_TYPE_MAX; ++i) { 294 gopt.amfs_auto_retrans[i] = -1; /* -1 means "never set before" */ 295 gopt.amfs_auto_timeo[i] = -1; /* -1 means "never set before" */ 296 } 297 298 /* cache duration */ 299 gopt.am_timeo = AM_TTL; 300 301 /* dismount interval */ 302 gopt.am_timeo_w = AM_TTL_W; 303 304 /* map reload intervl */ 305 gopt.map_reload_interval = ONE_HOUR; 306 307 /* 308 * various CFM_* flags that are on by default. 309 */ 310 gopt.flags = CFM_DEFAULT_FLAGS; 311 312#ifdef HAVE_MAP_HESIOD 313 /* Hesiod rhs zone */ 314 gopt.hesiod_base = "automount"; 315#endif /* HAVE_MAP_HESIOD */ 316 317#ifdef HAVE_MAP_LDAP 318 /* LDAP base */ 319 gopt.ldap_base = NULL; 320 321 /* LDAP host ports */ 322 gopt.ldap_hostports = NULL; 323 324 /* LDAP cache */ 325 gopt.ldap_cache_seconds = 0; 326 gopt.ldap_cache_maxmem = 131072; 327 328 /* LDAP protocol version */ 329 gopt.ldap_proto_version = 2; 330#endif /* HAVE_MAP_LDAP */ 331 332#ifdef HAVE_MAP_NIS 333 /* YP domain name */ 334 gopt.nis_domain = NULL; 335#endif /* HAVE_MAP_NIS */ 336} 337 338 339/* 340 * Lock process text and data segment in memory (after forking the daemon) 341 */ 342static void 343do_memory_locking(void) 344{ 345#if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL) 346 int locked_ok = 0; 347#else /* not HAVE_PLOCK and not HAVE_MLOCKALL */ 348 plog(XLOG_WARNING, "Process memory locking not supported by the OS"); 349#endif /* not HAVE_PLOCK and not HAVE_MLOCKALL */ 350#ifdef HAVE_PLOCK 351# ifdef _AIX 352 /* 353 * On AIX you must lower the stack size using ulimit() before calling 354 * plock. Otherwise plock will reserve a lot of memory space based on 355 * your maximum stack size limit. Since it is not easily possible to 356 * tell what should the limit be, I print a warning before calling 357 * plock(). See the manual pages for ulimit(1,3,4) on your AIX system. 358 */ 359 plog(XLOG_WARNING, "AIX: may need to lower stack size using ulimit(3) before calling plock"); 360# endif /* _AIX */ 361 if (!locked_ok && plock(PROCLOCK) != 0) 362 plog(XLOG_WARNING, "Couldn't lock process pages in memory using plock(): %m"); 363 else 364 locked_ok = 1; 365#endif /* HAVE_PLOCK */ 366#ifdef HAVE_MLOCKALL 367 if (!locked_ok && mlockall(MCL_CURRENT|MCL_FUTURE) != 0) 368 plog(XLOG_WARNING, "Couldn't lock process pages in memory using mlockall(): %m"); 369 else 370 locked_ok = 1; 371#endif /* HAVE_MLOCKALL */ 372#if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL) 373 if (locked_ok) 374 plog(XLOG_INFO, "Locked process pages in memory"); 375#endif /* HAVE_PLOCK || HAVE_MLOCKALL */ 376 377#if defined(HAVE_MADVISE) && defined(MADV_PROTECT) 378 madvise(NULL, 0, MADV_PROTECT); /* may be redundant of the above worked out */ 379#endif /* defined(HAVE_MADVISE) && defined(MADV_PROTECT) */ 380} 381 382 383int 384main(int argc, char *argv[]) 385{ 386 char *domdot, *verstr, *vertmp; 387 int ppid = 0; 388 int error; 389 char *progname = NULL; /* "amd" */ 390 char hostname[MAXHOSTNAMELEN + 1] = "localhost"; /* Hostname */ 391 392 /* 393 * Make sure some built-in assumptions are true before we start 394 */ 395 assert(sizeof(nfscookie) >= sizeof(u_int)); 396 assert(sizeof(int) >= 4); 397 398 /* 399 * Set processing status. 400 */ 401 amd_state = Start; 402 403 /* 404 * Determine program name 405 */ 406 if (argv[0]) { 407 progname = strrchr(argv[0], '/'); 408 if (progname && progname[1]) 409 progname++; 410 else 411 progname = argv[0]; 412 } 413 if (!progname) 414 progname = "amd"; 415 am_set_progname(progname); 416 417 /* 418 * Initialize process id. This is kept 419 * cached since it is used for generating 420 * and using file handles. 421 */ 422 am_set_mypid(); 423 424 /* 425 * Get local machine name 426 */ 427 if (gethostname(hostname, sizeof(hostname)) < 0) { 428 plog(XLOG_FATAL, "gethostname: %m"); 429 going_down(1); 430 return 1; 431 } 432 hostname[sizeof(hostname) - 1] = '\0'; 433 434 /* 435 * Check it makes sense 436 */ 437 if (!*hostname) { 438 plog(XLOG_FATAL, "host name is not set"); 439 going_down(1); 440 return 1; 441 } 442 443 /* 444 * Initialize global options structure. 445 */ 446 init_global_options(); 447 448 /* 449 * Partially initialize hostd[]. This 450 * is completed in get_args(). 451 */ 452 if ((domdot = strchr(hostname, '.'))) { 453 /* 454 * Hostname already contains domainname. 455 * Split out hostname and domainname 456 * components 457 */ 458 *domdot++ = '\0'; 459 hostdomain = domdot; 460 } 461 xstrlcpy(hostd, hostname, sizeof(hostd)); 462 am_set_hostname(hostname); 463 464 /* 465 * Setup signal handlers 466 */ 467 /* SIGINT: trap interrupts for shutdowns */ 468 setup_sighandler(SIGINT, sigterm); 469 /* SIGTERM: trap terminate so we can shutdown cleanly (some chance) */ 470 setup_sighandler(SIGTERM, sigterm); 471 /* SIGHUP: hangups tell us to reload the cache */ 472 setup_sighandler(SIGHUP, sighup); 473 /* 474 * SIGCHLD: trap Death-of-a-child. These allow us to pick up the exit 475 * status of backgrounded mounts. See "sched.c". 476 */ 477 setup_sighandler(SIGCHLD, sigchld); 478#ifdef HAVE_SIGACTION 479 /* construct global "masked_sigs" used in nfs_start.c */ 480 sigemptyset(&masked_sigs); 481 sigaddset(&masked_sigs, SIGINT); 482 sigaddset(&masked_sigs, SIGTERM); 483 sigaddset(&masked_sigs, SIGHUP); 484 sigaddset(&masked_sigs, SIGCHLD); 485#endif /* HAVE_SIGACTION */ 486 487 /* 488 * Fix-up any umask problems. Most systems default 489 * to 002 which is not too convenient for our purposes 490 */ 491 orig_umask = umask(0); 492 493 /* 494 * Figure out primary network name 495 */ 496 getwire(&PrimNetName, &PrimNetNum); 497 498 /* 499 * Determine command-line arguments. 500 * (Also initialize amd.conf parameters, maps, and more.) 501 */ 502 get_args(argc, argv); 503 504 /* 505 * Log version information. 506 */ 507 vertmp = get_version_string(); 508 verstr = strtok(vertmp, "\n"); 509 plog(XLOG_INFO, "AM-UTILS VERSION INFORMATION:"); 510 while (verstr) { 511 plog(XLOG_INFO, "%s", verstr); 512 verstr = strtok(NULL, "\n"); 513 } 514 XFREE(vertmp); 515 516 /* 517 * Get our own IP address so that we can mount the automounter. We pass 518 * localhost_address which could be used as the default localhost 519 * name/address in amu_get_myaddress(). 520 */ 521 amu_get_myaddress(&myipaddr, gopt.localhost_address); 522 plog(XLOG_INFO, "My ip addr is %s", inet_ntoa(myipaddr)); 523 524 /* avoid hanging on other NFS servers if started elsewhere */ 525 if (chdir("/") < 0) 526 plog(XLOG_INFO, "cannot chdir to /: %m"); 527 528 /* 529 * Now check we are root. 530 */ 531 if (geteuid() != 0) { 532 plog(XLOG_FATAL, "Must be root to mount filesystems (euid = %ld)", (long) geteuid()); 533 going_down(1); 534 return 1; 535 } 536 537#ifdef HAVE_MAP_NIS 538 /* 539 * If the domain was specified then bind it here 540 * to circumvent any default bindings that may 541 * be done in the C library. 542 */ 543 if (gopt.nis_domain && yp_bind(gopt.nis_domain)) { 544 plog(XLOG_FATAL, "Can't bind to NIS domain \"%s\"", gopt.nis_domain); 545 going_down(1); 546 return 1; 547 } 548#endif /* HAVE_MAP_NIS */ 549 550 if (amuDebug(D_DAEMON)) 551 ppid = daemon_mode(); 552 553 /* 554 * Lock process text and data segment in memory. 555 */ 556 if (gopt.flags & CFM_PROCESS_LOCK) { 557 do_memory_locking(); 558 } 559 560 do_mapc_reload = clocktime(NULL) + gopt.map_reload_interval; 561 562 /* 563 * Register automounter with system. 564 */ 565 error = mount_automounter(ppid); 566 if (error && ppid) 567 kill(ppid, SIGALRM); 568 569#ifdef HAVE_FS_AUTOFS 570 /* 571 * XXX this should be part of going_down(), but I can't move it there 572 * because it would be calling non-library code from the library... ugh 573 */ 574 if (amd_use_autofs) 575 destroy_autofs_service(); 576#endif /* HAVE_FS_AUTOFS */ 577 578 going_down(error); 579 580 abort(); 581 return 1; /* should never get here */ 582} 583