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