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