xref: /netbsd-src/external/bsd/am-utils/dist/amd/amd.c (revision 8bae5d409deb915cf7c8f0539fae22ff2cb8a313)
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