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