4 * Copyright (c) 1997-2009 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.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
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. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * File: am-utils/amd/amd.c
52 #endif /* HAVE_CONFIG_H */
56 struct amu_global_options gopt
; /* where global options are stored */
58 char pid_fsname
[SIZEOF_PID_FSNAME
]; /* "kiska.southseas.nz:(pid%d)" */
59 char *hostdomain
= "unknown.domain";
60 #define SIZEOF_HOSTD (2 * MAXHOSTNAMELEN + 1) /* Host+domain */
61 char hostd
[SIZEOF_HOSTD
]; /* Host+domain */
62 char *endian
= ARCH_ENDIAN
; /* Big or Little endian */
63 char *cpu
= HOST_CPU
; /* CPU type */
64 char *PrimNetName
; /* name of primary network */
65 char *PrimNetNum
; /* number of primary network */
67 int immediate_abort
; /* Should close-down unmounts be retried */
69 int select_intr_valid
;
72 struct amd_stats amd_stats
; /* Server statistics */
73 struct in_addr myipaddr
; /* (An) IP address of this host */
74 time_t do_mapc_reload
= 0; /* mapc_reload() call required? */
77 int amd_use_autofs
= 0;
78 #endif /* HAVE_FS_AUTOFS */
82 #endif /* HAVE_SIGACTION */
87 * SIGINT - tells amd to do a full shutdown, including unmounting all
89 * SIGTERM - tells amd to shutdown now. Just unmounts the automount nodes.
94 #ifdef REINSTALL_SIGNAL_HANDLER
96 #endif /* REINSTALL_SIGNAL_HANDLER */
100 immediate_abort
= 15;
104 immediate_abort
= -1;
105 /* fall through... */
108 plog(XLOG_WARNING
, "WARNING: automounter going down on signal %d", sig
);
111 if (select_intr_valid
)
112 longjmp(select_intr
, sig
);
117 * Hook for cache reload.
118 * When a SIGHUP arrives it schedules a call to mapc_reload
123 #ifdef REINSTALL_SIGNAL_HANDLER
125 #endif /* REINSTALL_SIGNAL_HANDLER */
128 dlog("spurious call to sighup");
130 * Force a reload by zero'ing the timer
132 if (amd_state
== Run
)
141 * This signal handler is called during Amd initialization. The parent
142 * forks a child to do all the hard automounting work, and waits for a
143 * SIGQUIT signal from the child. When the parent gets the signal it's
144 * supposed to call this handler and exit(3), thus completing the
145 * daemonizing process. Alas, on some systems, especially Linux 2.4/2.6
146 * with Glibc, exit(3) doesn't always terminate the parent process.
147 * Worse, the parent process now refuses to accept any more SIGQUIT
148 * signals -- they are blocked. What's really annoying is that this
149 * doesn't happen all the time, suggesting a race condition somewhere.
150 * (This happens even if I change the logic to use another signal.) I
151 * traced this to something which exit(3) does in addition to exiting the
152 * process, probably some atexit() stuff or other side-effects related to
153 * signal handling. Either way, since at this stage the parent process
154 * just needs to terminate, I'm simply calling _exit(2). Note also that
155 * the OpenGroup doesn't list exit(3) as a recommended "Base Interface"
156 * but they do list _exit(2) as one. This fix seems to work reliably all
157 * the time. -Erez (2/27/2005)
168 #ifdef HAVE_SIGACTION
169 struct sigaction sa
, osa
;
171 memset(&sa
, 0, sizeof(sa
));
172 sa
.sa_handler
= parent_exit
;
174 sigemptyset(&(sa
.sa_mask
));
175 sigaddset(&(sa
.sa_mask
), SIGQUIT
);
176 sigaction(SIGQUIT
, &sa
, &osa
);
177 #else /* not HAVE_SIGACTION */
178 signal(SIGQUIT
, parent_exit
);
179 #endif /* not HAVE_SIGACTION */
181 bgpid
= background();
185 * Now wait for the automount points to
190 /* should never reach here */
192 #ifdef HAVE_SIGACTION
193 sigaction(SIGQUIT
, &osa
, NULL
);
194 #else /* not HAVE_SIGACTION */
195 signal(SIGQUIT
, SIG_DFL
);
196 #endif /* not HAVE_SIGACTION */
199 * Record our pid to make it easier to kill the correct amd.
201 if (gopt
.flags
& CFM_PRINT_PID
) {
202 if (STREQ(gopt
.pid_file
, "/dev/stdout")) {
203 printf("%ld\n", (long) am_mypid
);
204 /* flush stdout, just in case */
208 mode_t prev_umask
= umask(0022); /* set secure temporary umask */
210 f
= fopen(gopt
.pid_file
, "w");
212 fprintf(f
, "%ld\n", (long) am_mypid
);
215 fprintf(stderr
, "cannot open %s (errno=%d)\n", gopt
.pid_file
, errno
);
217 umask(prev_umask
); /* restore umask */
222 * Pretend we are in the foreground again
227 * Dissociate from the controlling terminal
229 amu_release_controlling_tty();
236 * Initialize global options structure.
239 init_global_options(void)
241 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
242 static struct utsname un
;
243 #endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */
246 memset(&gopt
, 0, sizeof(struct amu_global_options
));
248 /* name of current architecture */
249 gopt
.arch
= HOST_ARCH
;
251 /* automounter temp dir */
252 gopt
.auto_dir
= "/a";
254 /* toplevel attribute cache timeout */
255 gopt
.auto_attrcache
= 0;
260 /* executable map timeout */
261 gopt
.exec_map_timeout
= AMFS_EXEC_MAP_TIMEOUT
;
264 * kernel architecture: this you must get from uname() if possible.
266 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
268 gopt
.karch
= un
.machine
;
270 #endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */
271 gopt
.karch
= HOST_ARCH
;
276 /* operating system name */
277 gopt
.op_sys
= HOST_OS_NAME
;
280 gopt
.op_sys_ver
= HOST_OS_VERSION
;
282 /* full OS name and version */
283 gopt
.op_sys_full
= HOST_OS
;
286 gopt
.op_sys_vendor
= HOST_VENDOR
;
289 gopt
.pid_file
= "/dev/stdout";
292 gopt
.sub_domain
= NULL
;
294 /* reset NFS (and toplvl) retransmit counter and retry interval */
295 for (i
=0; i
<AMU_TYPE_MAX
; ++i
) {
296 gopt
.amfs_auto_retrans
[i
] = -1; /* -1 means "never set before" */
297 gopt
.amfs_auto_timeo
[i
] = -1; /* -1 means "never set before" */
301 gopt
.am_timeo
= AM_TTL
;
303 /* dismount interval */
304 gopt
.am_timeo_w
= AM_TTL_W
;
306 /* map reload intervl */
307 gopt
.map_reload_interval
= ONE_HOUR
;
310 * various CFM_* flags that are on by default.
312 gopt
.flags
= CFM_DEFAULT_FLAGS
;
314 #ifdef HAVE_MAP_HESIOD
315 /* Hesiod rhs zone */
316 gopt
.hesiod_base
= "automount";
317 #endif /* HAVE_MAP_HESIOD */
321 gopt
.ldap_base
= NULL
;
323 /* LDAP host ports */
324 gopt
.ldap_hostports
= NULL
;
327 gopt
.ldap_cache_seconds
= 0;
328 gopt
.ldap_cache_maxmem
= 131072;
330 /* LDAP protocol version */
331 gopt
.ldap_proto_version
= 2;
332 #endif /* HAVE_MAP_LDAP */
336 gopt
.nis_domain
= NULL
;
337 #endif /* HAVE_MAP_NIS */
342 * Lock process text and data segment in memory (after forking the daemon)
345 do_memory_locking(void)
347 #if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL)
349 #else /* not HAVE_PLOCK and not HAVE_MLOCKALL */
350 plog(XLOG_WARNING
, "Process memory locking not supported by the OS");
351 #endif /* not HAVE_PLOCK and not HAVE_MLOCKALL */
355 * On AIX you must lower the stack size using ulimit() before calling
356 * plock. Otherwise plock will reserve a lot of memory space based on
357 * your maximum stack size limit. Since it is not easily possible to
358 * tell what should the limit be, I print a warning before calling
359 * plock(). See the manual pages for ulimit(1,3,4) on your AIX system.
361 plog(XLOG_WARNING
, "AIX: may need to lower stack size using ulimit(3) before calling plock");
363 if (!locked_ok
&& plock(PROCLOCK
) != 0)
364 plog(XLOG_WARNING
, "Couldn't lock process pages in memory using plock(): %m");
367 #endif /* HAVE_PLOCK */
369 if (!locked_ok
&& mlockall(MCL_CURRENT
|MCL_FUTURE
) != 0)
370 plog(XLOG_WARNING
, "Couldn't lock process pages in memory using mlockall(): %m");
373 #endif /* HAVE_MLOCKALL */
374 #if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL)
376 plog(XLOG_INFO
, "Locked process pages in memory");
377 #endif /* HAVE_PLOCK || HAVE_MLOCKALL */
379 #if defined(HAVE_MADVISE) && defined(MADV_PROTECT)
380 madvise(NULL
, 0, MADV_PROTECT
); /* may be redundant of the above worked out */
381 #endif /* defined(HAVE_MADVISE) && defined(MADV_PROTECT) */
386 main(int argc
, char *argv
[])
388 char *domdot
, *verstr
, *vertmp
;
391 char *progname
= NULL
; /* "amd" */
392 char hostname
[MAXHOSTNAMELEN
+ 1] = "localhost"; /* Hostname */
395 * Make sure some built-in assumptions are true before we start
397 assert(sizeof(nfscookie
) >= sizeof(u_int
));
398 assert(sizeof(int) >= 4);
401 * Set processing status.
406 * Determine program name
409 progname
= strrchr(argv
[0], '/');
410 if (progname
&& progname
[1])
417 am_set_progname(progname
);
420 * Initialize process id. This is kept
421 * cached since it is used for generating
422 * and using file handles.
427 * Get local machine name
429 if (gethostname(hostname
, sizeof(hostname
)) < 0) {
430 plog(XLOG_FATAL
, "gethostname: %m");
433 hostname
[sizeof(hostname
) - 1] = '\0';
436 * Check it makes sense
439 plog(XLOG_FATAL
, "host name is not set");
444 * Initialize global options structure.
446 init_global_options();
449 * Partially initialize hostd[]. This
450 * is completed in get_args().
452 if ((domdot
= strchr(hostname
, '.'))) {
454 * Hostname already contains domainname.
455 * Split out hostname and domainname
461 xstrlcpy(hostd
, hostname
, sizeof(hostd
));
462 am_set_hostname(hostname
);
465 * Setup signal handlers
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
);
474 * SIGCHLD: trap Death-of-a-child. These allow us to pick up the exit
475 * status of backgrounded mounts. See "sched.c".
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 */
488 * Fix-up any umask problems. Most systems default
489 * to 002 which is not too convenient for our purposes
491 orig_umask
= umask(0);
494 * Figure out primary network name
496 getwire(&PrimNetName
, &PrimNetNum
);
499 * Determine command-line arguments.
500 * (Also initialize amd.conf parameters, maps, and more.)
502 get_args(argc
, argv
);
505 * Log version information.
507 vertmp
= get_version_string();
508 verstr
= strtok(vertmp
, "\n");
509 plog(XLOG_INFO
, "AM-UTILS VERSION INFORMATION:");
511 plog(XLOG_INFO
, "%s", verstr
);
512 verstr
= strtok(NULL
, "\n");
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().
521 amu_get_myaddress(&myipaddr
, gopt
.localhost_address
);
522 plog(XLOG_INFO
, "My ip addr is %s", inet_ntoa(myipaddr
));
524 /* avoid hanging on other NFS servers if started elsewhere */
526 plog(XLOG_INFO
, "cannot chdir to /: %m");
529 * Now check we are root.
531 if (geteuid() != 0) {
532 plog(XLOG_FATAL
, "Must be root to mount filesystems (euid = %ld)", (long) geteuid());
538 * If the domain was specified then bind it here
539 * to circumvent any default bindings that may
540 * be done in the C library.
542 if (gopt
.nis_domain
&& yp_bind(gopt
.nis_domain
)) {
543 plog(XLOG_FATAL
, "Can't bind to NIS domain \"%s\"", gopt
.nis_domain
);
546 #endif /* HAVE_MAP_NIS */
548 if (amuDebug(D_DAEMON
))
549 ppid
= daemon_mode();
552 * Lock process text and data segment in memory.
554 if (gopt
.flags
& CFM_PROCESS_LOCK
) {
558 do_mapc_reload
= clocktime(NULL
) + gopt
.map_reload_interval
;
561 * Register automounter with system.
563 error
= mount_automounter(ppid
);
567 #ifdef HAVE_FS_AUTOFS
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
573 destroy_autofs_service();
574 #endif /* HAVE_FS_AUTOFS */
579 return 1; /* should never get here */