xref: /netbsd/external/bsd/am-utils/dist/amd/amd.c (revision 6550d01e)
1 /*	$NetBSD: amd.c,v 1.1.1.2 2009/03/20 20:26:48 christos Exp $	*/
2 
3 /*
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.
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. 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.
28  *
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
39  * SUCH DAMAGE.
40  *
41  *
42  * File: am-utils/amd/amd.c
43  *
44  */
45 
46 /*
47  * Automounter
48  */
49 
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif /* HAVE_CONFIG_H */
53 #include <am_defs.h>
54 #include <amd.h>
55 
56 struct amu_global_options gopt;	/* where global options are stored */
57 
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 */
66 
67 int immediate_abort;		/* Should close-down unmounts be retried */
68 int orig_umask = 022;
69 int select_intr_valid;
70 
71 jmp_buf select_intr;
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? */
75 
76 #ifdef HAVE_FS_AUTOFS
77 int amd_use_autofs = 0;
78 #endif /* HAVE_FS_AUTOFS */
79 
80 #ifdef HAVE_SIGACTION
81 sigset_t masked_sigs;
82 #endif /* HAVE_SIGACTION */
83 
84 
85 /*
86  * Signal handler:
87  * SIGINT - tells amd to do a full shutdown, including unmounting all
88  *       filesystem.
89  * SIGTERM - tells amd to shutdown now.  Just unmounts the automount nodes.
90  */
91 static RETSIGTYPE
92 sigterm(int sig)
93 {
94 #ifdef REINSTALL_SIGNAL_HANDLER
95   signal(sig, sigterm);
96 #endif /* REINSTALL_SIGNAL_HANDLER */
97 
98   switch (sig) {
99   case SIGINT:
100     immediate_abort = 15;
101     break;
102 
103   case SIGTERM:
104     immediate_abort = -1;
105     /* fall through... */
106 
107   default:
108     plog(XLOG_WARNING, "WARNING: automounter going down on signal %d", sig);
109     break;
110   }
111   if (select_intr_valid)
112     longjmp(select_intr, sig);
113 }
114 
115 
116 /*
117  * Hook for cache reload.
118  * When a SIGHUP arrives it schedules a call to mapc_reload
119  */
120 static RETSIGTYPE
121 sighup(int sig)
122 {
123 #ifdef REINSTALL_SIGNAL_HANDLER
124   signal(sig, sighup);
125 #endif /* REINSTALL_SIGNAL_HANDLER */
126 
127   if (sig != SIGHUP)
128     dlog("spurious call to sighup");
129   /*
130    * Force a reload by zero'ing the timer
131    */
132   if (amd_state == Run)
133     do_mapc_reload = 0;
134 }
135 
136 
137 static RETSIGTYPE
138 parent_exit(int sig)
139 {
140   /*
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)
158    */
159   _exit(0);
160 }
161 
162 
163 static int
164 daemon_mode(void)
165 {
166   int bgpid;
167 
168 #ifdef HAVE_SIGACTION
169   struct sigaction sa, osa;
170 
171   memset(&sa, 0, sizeof(sa));
172   sa.sa_handler = parent_exit;
173   sa.sa_flags = 0;
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 */
180 
181   bgpid = background();
182 
183   if (bgpid != 0) {
184     /*
185      * Now wait for the automount points to
186      * complete.
187      */
188     for (;;)
189       pause();
190     /* should never reach here */
191   }
192 #ifdef HAVE_SIGACTION
193   sigaction(SIGQUIT, &osa, NULL);
194 #else /* not HAVE_SIGACTION */
195   signal(SIGQUIT, SIG_DFL);
196 #endif /* not HAVE_SIGACTION */
197 
198   /*
199    * Record our pid to make it easier to kill the correct amd.
200    */
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 */
205       fflush(stdout);
206     } else {
207       FILE *f;
208       mode_t prev_umask = umask(0022); /* set secure temporary umask */
209 
210       f = fopen(gopt.pid_file, "w");
211       if (f) {
212 	fprintf(f, "%ld\n", (long) am_mypid);
213 	(void) fclose(f);
214       } else {
215 	fprintf(stderr, "cannot open %s (errno=%d)\n", gopt.pid_file, errno);
216       }
217       umask(prev_umask);	/* restore umask */
218     }
219   }
220 
221   /*
222    * Pretend we are in the foreground again
223    */
224   foreground = 1;
225 
226   /*
227    * Dissociate from the controlling terminal
228    */
229   amu_release_controlling_tty();
230 
231   return getppid();
232 }
233 
234 
235 /*
236  * Initialize global options structure.
237  */
238 static void
239 init_global_options(void)
240 {
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) */
244   int i;
245 
246   memset(&gopt, 0, sizeof(struct amu_global_options));
247 
248   /* name of current architecture */
249   gopt.arch = HOST_ARCH;
250 
251   /* automounter temp dir */
252   gopt.auto_dir = "/a";
253 
254   /* toplevel attribute cache timeout */
255   gopt.auto_attrcache = 0;
256 
257   /* cluster name */
258   gopt.cluster = NULL;
259 
260   /* executable map timeout */
261   gopt.exec_map_timeout = AMFS_EXEC_MAP_TIMEOUT;
262 
263   /*
264    * kernel architecture: this you must get from uname() if possible.
265    */
266 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
267   if (uname(&un) >= 0)
268     gopt.karch = un.machine;
269   else
270 #endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */
271     gopt.karch = HOST_ARCH;
272 
273   /* amd log file */
274   gopt.logfile = NULL;
275 
276   /* operating system name */
277   gopt.op_sys = HOST_OS_NAME;
278 
279   /* OS version */
280   gopt.op_sys_ver = HOST_OS_VERSION;
281 
282   /* full OS name and version */
283   gopt.op_sys_full = HOST_OS;
284 
285   /* OS version */
286   gopt.op_sys_vendor = HOST_VENDOR;
287 
288   /* pid file */
289   gopt.pid_file = "/dev/stdout";
290 
291   /* local domain */
292   gopt.sub_domain = NULL;
293 
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" */
298   }
299 
300   /* cache duration */
301   gopt.am_timeo = AM_TTL;
302 
303   /* dismount interval */
304   gopt.am_timeo_w = AM_TTL_W;
305 
306   /* map reload intervl */
307   gopt.map_reload_interval = ONE_HOUR;
308 
309   /*
310    * various CFM_* flags that are on by default.
311    */
312   gopt.flags = CFM_DEFAULT_FLAGS;
313 
314 #ifdef HAVE_MAP_HESIOD
315   /* Hesiod rhs zone */
316   gopt.hesiod_base = "automount";
317 #endif /* HAVE_MAP_HESIOD */
318 
319 #ifdef HAVE_MAP_LDAP
320   /* LDAP base */
321   gopt.ldap_base = NULL;
322 
323   /* LDAP host ports */
324   gopt.ldap_hostports = NULL;
325 
326   /* LDAP cache */
327   gopt.ldap_cache_seconds = 0;
328   gopt.ldap_cache_maxmem = 131072;
329 
330   /* LDAP protocol version */
331   gopt.ldap_proto_version = 2;
332 #endif /* HAVE_MAP_LDAP */
333 
334 #ifdef HAVE_MAP_NIS
335   /* YP domain name */
336   gopt.nis_domain = NULL;
337 #endif /* HAVE_MAP_NIS */
338 }
339 
340 
341 /*
342  * Lock process text and data segment in memory (after forking the daemon)
343  */
344 static void
345 do_memory_locking(void)
346 {
347 #if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL)
348   int locked_ok = 0;
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 */
352 #ifdef HAVE_PLOCK
353 # ifdef _AIX
354   /*
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.
360    */
361   plog(XLOG_WARNING, "AIX: may need to lower stack size using ulimit(3) before calling plock");
362 # endif /* _AIX */
363   if (!locked_ok && plock(PROCLOCK) != 0)
364     plog(XLOG_WARNING, "Couldn't lock process pages in memory using plock(): %m");
365   else
366     locked_ok = 1;
367 #endif /* HAVE_PLOCK */
368 #ifdef HAVE_MLOCKALL
369   if (!locked_ok && mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
370     plog(XLOG_WARNING, "Couldn't lock process pages in memory using mlockall(): %m");
371   else
372     locked_ok = 1;
373 #endif /* HAVE_MLOCKALL */
374 #if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL)
375   if (locked_ok)
376     plog(XLOG_INFO, "Locked process pages in memory");
377 #endif /* HAVE_PLOCK || HAVE_MLOCKALL */
378 
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) */
382 }
383 
384 
385 int
386 main(int argc, char *argv[])
387 {
388   char *domdot, *verstr, *vertmp;
389   int ppid = 0;
390   int error;
391   char *progname = NULL;		/* "amd" */
392   char hostname[MAXHOSTNAMELEN + 1] = "localhost"; /* Hostname */
393 
394   /*
395    * Make sure some built-in assumptions are true before we start
396    */
397   assert(sizeof(nfscookie) >= sizeof(u_int));
398   assert(sizeof(int) >= 4);
399 
400   /*
401    * Set processing status.
402    */
403   amd_state = Start;
404 
405   /*
406    * Determine program name
407    */
408   if (argv[0]) {
409     progname = strrchr(argv[0], '/');
410     if (progname && progname[1])
411       progname++;
412     else
413       progname = argv[0];
414   }
415   if (!progname)
416     progname = "amd";
417   am_set_progname(progname);
418 
419   /*
420    * Initialize process id.  This is kept
421    * cached since it is used for generating
422    * and using file handles.
423    */
424   am_set_mypid();
425 
426   /*
427    * Get local machine name
428    */
429   if (gethostname(hostname, sizeof(hostname)) < 0) {
430     plog(XLOG_FATAL, "gethostname: %m");
431     going_down(1);
432   }
433   hostname[sizeof(hostname) - 1] = '\0';
434 
435   /*
436    * Check it makes sense
437    */
438   if (!*hostname) {
439     plog(XLOG_FATAL, "host name is not set");
440     going_down(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   }
535 
536 #ifdef HAVE_MAP_NIS
537   /*
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.
541    */
542   if (gopt.nis_domain && yp_bind(gopt.nis_domain)) {
543     plog(XLOG_FATAL, "Can't bind to NIS domain \"%s\"", gopt.nis_domain);
544     going_down(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