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