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
sigterm(int sig)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
sighup(int sig)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
parent_exit(int sig)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
daemon_mode(void)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
init_global_options(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
do_memory_locking(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
main(int argc,char * argv[])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