xref: /netbsd-src/external/bsd/am-utils/dist/hlfsd/hlfsd.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
1 /*	$NetBSD: hlfsd.c,v 1.2 2013/10/20 03:13:44 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/hlfsd/hlfsd.c
43  *
44  * HLFSD was written at Columbia University Computer Science Department, by
45  * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
46  * It is being distributed under the same terms and conditions as amd does.
47  */
48 
49 #ifdef HAVE_CONFIG_H
50 # include <config.h>
51 #endif /* HAVE_CONFIG_H */
52 #include <am_defs.h>
53 #include <hlfsd.h>
54 
55 /*
56  * STATIC VARIABLES:
57  */
58 static RETSIGTYPE proceed(int);
59 static RETSIGTYPE reaper(int);
60 static RETSIGTYPE reload(int);
61 static char *hlfs_group = DEFAULT_HLFS_GROUP;
62 static char default_dir_name[] = DEFAULT_DIRNAME;
63 static char *dir_name = default_dir_name;
64 static int printpid = 0;
65 static int stoplight = 0;
66 static void hlfsd_init(void);
67 static void usage(void);
68 
69 static struct itimerval reloadinterval = {
70   {DEFAULT_INTERVAL, 0},
71   {DEFAULT_INTERVAL, 0}
72 };
73 
74 /*
75  * default mount options.
76  */
77 static char default_mntopts[] = "ro,noac";
78 
79 /*
80  * GLOBALS:
81  */
82 SVCXPRT *nfsxprt;
83 char *alt_spooldir = ALT_SPOOLDIR;
84 char *home_subdir = HOME_SUBDIR;
85 char *logfile = DEFAULT_LOGFILE;
86 char *passwdfile = NULL;	/* alternate passwd file to use */
87 char *slinkname = NULL;
88 char hostname[MAXHOSTNAMELEN + 1] = "localhost";
89 u_int cache_interval = DEFAULT_CACHE_INTERVAL;
90 gid_t hlfs_gid = (gid_t) INVALIDID;
91 int masterpid = 0;
92 int noverify = 0;
93 int orig_umask = 022;
94 int serverpid = 0;
95 nfstime startup;
96 u_short nfs_port;
97 
98 /* symbol must be available always */
99 #ifdef MNTTAB_FILE_NAME
100 char *mnttab_file_name = MNTTAB_FILE_NAME;
101 #else /* not MNTTAB_FILE_NAME */
102 char *mnttab_file_name = NULL;
103 #endif /* not MNTTAB_FILE_NAME */
104 
105 /* forward declarations */
106 void hlfsd_going_down(int rc);
107 void fatalerror(char *str);
108 
109 
110 static void
111 usage(void)
112 {
113   fprintf(stderr,
114 	  "Usage: %s [-Cfhnpv] [-a altdir] [-c cache-interval] [-g group]\n",
115 	  am_get_progname());
116   fprintf(stderr, "\t[-i interval] [-l logfile] [-o mntopts] [-P passwdfile]\n");
117   show_opts('x', xlog_opt);
118 #ifdef DEBUG
119   show_opts('D', dbg_opt);
120 #endif /* DEBUG */
121   fprintf(stderr, "\t[dir_name [subdir]]\n");
122   exit(2);
123 }
124 
125 
126 void
127 fatalerror(char *str)
128 {
129 #define ERRM ": %m"
130   size_t l = strlen(str) + sizeof(ERRM) - 1;
131   char *tmp = strnsave(str, l);
132   xstrlcat(tmp, ERRM, l);
133   fatal(tmp);
134 }
135 
136 
137 int
138 main(int argc, char *argv[])
139 {
140   char *dot;
141   char *mntopts = (char *) NULL;
142   char hostpid_fs[MAXHOSTNAMELEN + 1 + 16];	/* room for ":(pid###)" */
143   char progpid_fs[PROGNAMESZ + 1 + 11];		/* room for ":pid" */
144   char preopts[128];
145   char *progname;
146   int forcecache = 0;
147   int forcefast = 0;
148   int genflags = 0;
149   int opt, ret;
150   int opterrs = 0;
151   int retry;
152   int soNFS;			/* NFS socket */
153   mntent_t mnt;
154   nfs_args_t nfs_args;
155   am_nfs_handle_t anh;
156   struct dirent *direntry;
157   struct group *grp;
158   struct stat stmodes;
159   DIR *mountdir;
160   MTYPE_TYPE type = MOUNT_TYPE_NFS;
161 
162 #ifdef HAVE_SIGACTION
163   struct sigaction sa;
164 #endif /* not HAVE_SIGACTION */
165 
166 #ifndef HAVE_TRANSPORT_TYPE_TLI
167   struct sockaddr_in localsocket;
168 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
169 
170 
171   /* get program name and truncate so we don't overflow progpid_fs */
172 
173   if ((progname = strrchr(argv[0], '/')) != NULL)
174     progname++;
175   else
176     progname = argv[0];
177   if ((int) strlen(progname) > PROGNAMESZ) /* truncate to reasonable size */
178     progname[PROGNAMESZ] = '\0';
179   am_set_progname(progname);
180 
181   while ((opt = getopt(argc, argv, "a:c:CD:fg:hi:l:no:pP:x:v")) != -1)
182     switch (opt) {
183 
184     case 'a':
185       if (!optarg || optarg[0] != '/') {
186 	printf("%s: invalid directory for -a: %s\n",
187 	       am_get_progname(), optarg);
188 	exit(3);
189       }
190       alt_spooldir = optarg;
191       break;
192 
193     case 'c':
194       if (!atoi(optarg)) {
195 	printf("%s: invalid interval for -c: %s\n",
196 	       am_get_progname(), optarg);
197 	exit(3);
198       }
199       cache_interval = atoi(optarg);
200       break;
201 
202     case 'C':
203       forcecache++;
204       break;
205 
206     case 'f':
207       forcefast++;
208       break;
209 
210     case 'g':
211       hlfs_group = optarg;
212       break;
213 
214     case 'i':
215       if (!atoi(optarg)) {
216 	printf("%s: invalid interval for -i: %s\n",
217 	       am_get_progname(), optarg);
218 	exit(3);
219       }
220       reloadinterval.it_interval.tv_sec = atoi(optarg);
221       reloadinterval.it_value.tv_sec = atoi(optarg);
222       break;
223 
224     case 'l':
225       logfile = optarg;
226       break;
227 
228     case 'n':
229       noverify++;
230       break;
231 
232     case 'o':
233       mntopts = optarg;
234       break;
235 
236     case 'p':
237       printpid++;
238       break;
239 
240     case 'P':
241       passwdfile = optarg;
242       break;
243 
244     case 'v':
245       fprintf(stderr, "%s\n", HLFSD_VERSION);
246       exit(0);
247 
248     case 'x':
249       opterrs += switch_option(optarg);
250       break;
251 
252     case 'D':
253 #ifdef DEBUG
254       opterrs += debug_option(optarg);
255 #else /* not DEBUG */
256       fprintf(stderr, "%s: not compiled with DEBUG -- sorry.\n", am_get_progname());
257 #endif /* not DEBUG */
258       break;
259 
260     case 'h':
261     case '?':
262       opterrs++;
263     }
264 
265   /* need my pid before any dlog/plog */
266   am_set_mypid();
267 #ifdef DEBUG
268   switch_option("debug");
269 #endif /* DEBUG */
270 
271 /*
272  * Terminate if did not ask to forcecache (-C) and hlfsd would not be able
273  * to set the minimum cache intervals.
274  */
275 #if !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_NFS_ARGS_T_ACREGMIN)
276   if (!forcecache) {
277     fprintf(stderr, "%s: will not be able to turn off attribute caches.\n", am_get_progname());
278     exit(1);
279   }
280 #endif /* !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_NFS_ARGS_T_ACREGMIN) */
281 
282 
283   switch (argc - optind) {
284   case 2:
285     home_subdir = argv[optind + 1];
286   case 1:
287     dir_name = argv[optind];
288   case 0:
289     break;
290   default:
291     opterrs++;
292   }
293 
294   if (opterrs)
295     usage();
296 
297   /* ensure that only root can run hlfsd */
298   if (geteuid()) {
299     fprintf(stderr, "hlfsd can only be run as root\n");
300     exit(1);
301   }
302   setbuf(stdout, (char *) NULL);
303   umask(0);
304 
305   /* find gid for hlfs_group */
306   if ((grp = getgrnam(hlfs_group)) == (struct group *) NULL) {
307     fprintf(stderr, "%s: cannot get gid for group \"%s\".\n",
308 	    am_get_progname(), hlfs_group);
309   } else {
310     hlfs_gid = grp->gr_gid;
311   }
312 
313   /* get hostname for logging and open log before we reset umask */
314   gethostname(hostname, sizeof(hostname));
315   hostname[sizeof(hostname) - 1] = '\0';
316   if ((dot = strchr(hostname, '.')) != NULL)
317     *dot = '\0';
318   orig_umask = umask(0);
319   if (logfile)
320     switch_to_logfile(logfile, orig_umask, 0);
321 
322 #ifndef MOUNT_TABLE_ON_FILE
323   if (amuDebug(D_MTAB))
324     dlog("-D mtab option ignored");
325 #endif /* not MOUNT_TABLE_ON_FILE */
326 
327   /* avoid hanging on other NFS servers if started elsewhere */
328   if (chdir("/") < 0)
329     fatal("cannot chdir to /: %m");
330 
331   if (geteuid() != 0)
332     fatal("must be root to mount filesystems");
333 
334   /*
335    * dir_name must match "^(/.*)/([^/]+)$", and is split at last '/' with
336    * slinkname = `basename $dir_name` - requires dir_name be writable
337    */
338 
339   if (dir_name[0] != '/'
340       || ((slinkname = strrchr(dir_name, '/')), *slinkname++ = '\0',
341 	  (dir_name[0] == '\0' || slinkname[0] == '\0'))) {
342     if (slinkname)
343       *--slinkname = '/';
344     printf("%s: invalid mount directory/link %s\n",
345 	   am_get_progname(), dir_name);
346     exit(3);
347   }
348 
349   if (!forcefast) {
350     /* make sure mount point exists and is at least mode 555 */
351     if (stat(dir_name, &stmodes) < 0)
352       if (errno != ENOENT || mkdirs(dir_name, 0555) < 0
353 	  || stat(dir_name, &stmodes) < 0)
354 	fatalerror(dir_name);
355 
356     if ((stmodes.st_mode & 0555) != 0555) {
357       fprintf(stderr, "%s: directory %s not read/executable\n",
358 	      am_get_progname(), dir_name);
359       plog(XLOG_WARNING, "directory %s not read/executable",
360 	   dir_name);
361     }
362 
363     /* warn if extraneous stuff will be hidden by mount */
364     if ((mountdir = opendir(dir_name)) == NULL)
365       fatalerror(dir_name);
366 
367     while ((direntry = readdir(mountdir)) != NULL) {
368       if (!NSTREQ(".", direntry->d_name, NAMLEN(direntry)) &&
369 	  !NSTREQ("..", direntry->d_name, NAMLEN(direntry)) &&
370 	  !NSTREQ(slinkname, direntry->d_name, NAMLEN(direntry)))
371 	break;
372     }
373 
374     if (direntry != NULL) {
375       fprintf(stderr, "%s: %s/%s will be hidden by mount\n",
376 	      am_get_progname(), dir_name, direntry->d_name);
377       plog(XLOG_WARNING, "%s/%s will be hidden by mount\n",
378 	   dir_name, direntry->d_name);
379     }
380     closedir(mountdir);
381 
382     /* make sure alternate spool dir exists */
383     if ((errno = mkdirs(alt_spooldir, OPEN_SPOOLMODE))) {
384       fprintf(stderr, "%s: cannot create alternate dir ",
385 	      am_get_progname());
386       perror(alt_spooldir);
387       plog(XLOG_ERROR, "cannot create alternate dir %s: %m",
388 	   alt_spooldir);
389     }
390     chmod(alt_spooldir, OPEN_SPOOLMODE);
391 
392     /* create failsafe link to alternate spool directory */
393     *(slinkname-1) = '/';	/* unsplit dir_name to include link */
394     if (lstat(dir_name, &stmodes) == 0 &&
395 	(stmodes.st_mode & S_IFMT) != S_IFLNK) {
396       fprintf(stderr, "%s: failsafe %s not a symlink\n",
397 	      am_get_progname(), dir_name);
398       plog(XLOG_WARNING, "failsafe %s not a symlink\n",
399 	   dir_name);
400     } else {
401       unlink(dir_name);
402 
403       if (symlink(alt_spooldir, dir_name) < 0) {
404 	fprintf(stderr,
405 		"%s: cannot create failsafe symlink %s -> ",
406 		am_get_progname(), dir_name);
407 	perror(alt_spooldir);
408 	plog(XLOG_WARNING,
409 	     "cannot create failsafe symlink %s -> %s: %m",
410 	     dir_name, alt_spooldir);
411       }
412     }
413 
414     *(slinkname-1) = '\0';	/* resplit dir_name */
415   } /* end of "if (!forcefast) {" */
416 
417   /*
418    * Register hlfsd as an nfs service with the portmapper.
419    */
420 #ifdef HAVE_TRANSPORT_TYPE_TLI
421   ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2);
422 #else /* not HAVE_TRANSPORT_TYPE_TLI */
423   ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2);
424 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
425   if (ret != 0)
426     fatal("cannot create NFS service");
427 
428 #ifdef HAVE_SIGACTION
429   sa.sa_handler = proceed;
430   sa.sa_flags = 0;
431   sigemptyset(&(sa.sa_mask));
432   sigaddset(&(sa.sa_mask), SIGUSR2);
433   sigaction(SIGUSR2, &sa, NULL);
434 #else /* not HAVE_SIGACTION */
435   signal(SIGUSR2, proceed);
436 #endif /* not HAVE_SIGACTION */
437 
438   plog(XLOG_INFO, "Initializing hlfsd...");
439   hlfsd_init();			/* start up child (forking) to run svc_run */
440 
441 #ifdef HAVE_SIGACTION
442   sa.sa_handler = reaper;
443   sa.sa_flags = 0;
444   sigemptyset(&(sa.sa_mask));
445   sigaddset(&(sa.sa_mask), SIGCHLD);
446   sigaction(SIGCHLD, &sa, NULL);
447 #else /* not HAVE_SIGACTION */
448   signal(SIGCHLD, reaper);
449 #endif /* not HAVE_SIGACTION */
450 
451   /*
452    * In the parent, if -D nodaemon, we don't need to
453    * set this signal handler.
454    */
455   if (amuDebug(D_DAEMON)) {
456     while (stoplight != SIGUSR2) {
457       plog(XLOG_INFO, "parent waits for child to setup (stoplight=%d)", stoplight);
458 #ifdef HAVE_SIGSUSPEND
459       {
460 	sigset_t mask;
461 	sigemptyset(&mask);
462 	(void)sigsuspend(&mask);	/* wait for child to set up */
463       }
464 #else /* not HAVE_SIGSUSPEND */
465       (void)sigpause(0);		/* wait for child to set up */
466 #endif /* not HAVE_SIGSUSPEND */
467       sleep(1);
468     }
469   }
470 
471   /*
472    * setup options to mount table (/etc/{mtab,mnttab}) entry
473    */
474   xsnprintf(hostpid_fs, sizeof(hostpid_fs),
475 	    "%s:(pid%d)", hostname, masterpid);
476   memset((char *) &mnt, 0, sizeof(mnt));
477   mnt.mnt_dir = dir_name;	/* i.e., "/mail" */
478   mnt.mnt_fsname = hostpid_fs;
479   if (mntopts) {
480     mnt.mnt_opts = mntopts;
481   } else {
482     xstrlcpy(preopts, default_mntopts, sizeof(preopts));
483     /*
484      * Turn off all kinds of attribute and symlink caches as
485      * much as possible.  Also make sure that mount does not
486      * show up to df.
487      */
488 #ifdef MNTTAB_OPT_INTR
489     xstrlcat(preopts, ",", sizeof(preopts));
490     xstrlcat(preopts, MNTTAB_OPT_INTR, sizeof(preopts));
491 #endif /* MNTTAB_OPT_INTR */
492 #ifdef MNTTAB_OPT_IGNORE
493     xstrlcat(preopts, ",", sizeof(preopts));
494     xstrlcat(preopts, MNTTAB_OPT_IGNORE, sizeof(preopts));
495 #endif /* MNTTAB_OPT_IGNORE */
496 #ifdef MNT2_GEN_OPT_CACHE
497     xstrlcat(preopts, ",nocache", sizeof(preopts));
498 #endif /* MNT2_GEN_OPT_CACHE */
499 #ifdef MNT2_NFS_OPT_SYMTTL
500     xstrlcat(preopts, ",symttl=0", sizeof(preopts));
501 #endif /* MNT2_NFS_OPT_SYMTTL */
502     mnt.mnt_opts = preopts;
503   }
504 
505   /*
506    * Make sure that amd's top-level NFS mounts are hidden by default
507    * from df.
508    * If they don't appear to support the either the "ignore" mnttab
509    * option entry, or the "auto" one, set the mount type to "nfs".
510    */
511 #ifdef HIDE_MOUNT_TYPE
512   mnt.mnt_type = HIDE_MOUNT_TYPE;
513 #else /* not HIDE_MOUNT_TYPE */
514   mnt.mnt_type = "nfs";
515 #endif /* not HIDE_MOUNT_TYPE */
516   /* some systems don't have a mount type, but a mount flag */
517 
518 #ifndef HAVE_TRANSPORT_TYPE_TLI
519   amu_get_myaddress(&localsocket.sin_addr, NULL);
520   localsocket.sin_family = AF_INET;
521   localsocket.sin_port = htons(nfsxprt->xp_port);
522 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
523 
524   /*
525    * Update hostname field.
526    * Make some name prog:pid (i.e., hlfsd:174) for hostname
527    */
528   xsnprintf(progpid_fs, sizeof(progpid_fs),
529 	    "%s:%d", am_get_progname(), masterpid);
530 
531   /* Most kernels have a name length restriction. */
532   if ((int) strlen(progpid_fs) >= (int) MAXHOSTNAMELEN)
533     xstrlcpy(progpid_fs + MAXHOSTNAMELEN - 3, "..",
534 	     sizeof(progpid_fs) - MAXHOSTNAMELEN + 3);
535 
536   genflags = compute_mount_flags(&mnt);
537 
538   retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
539   if (retry <= 0)
540     retry = 1;			/* XXX */
541 
542   memmove(&anh.v2, root_fhp, sizeof(*root_fhp));
543 #ifdef HAVE_TRANSPORT_TYPE_TLI
544   compute_nfs_args(&nfs_args,
545 		   &mnt,
546 		   genflags,
547 		   nfsncp,
548 		   NULL,	/* remote host IP addr is set below */
549 		   NFS_VERSION,	/* version 2 */
550 		   "udp",	/* XXX: shouldn't this be "udp"? */
551 		   &anh,
552 		   progpid_fs,	/* host name for kernel */
553 		   hostpid_fs); /* filesystem name for kernel */
554   /*
555    * IMPORTANT: set the correct IP address AFTERWARDS.  It cannot
556    * be done using the normal mechanism of compute_nfs_args(), because
557    * that one will allocate a new address and use NFS_SA_DREF() to copy
558    * parts to it, while assuming that the ip_addr passed is always
559    * a "struct sockaddr_in".  That assumption is incorrect on TLI systems,
560    * because they define a special macro HOST_SELF which is DIFFERENT
561    * than localhost (127.0.0.1)!
562    */
563   nfs_args.addr = &nfsxprt->xp_ltaddr;
564 #else /* not HAVE_TRANSPORT_TYPE_TLI */
565   compute_nfs_args(&nfs_args,
566 		   &mnt,
567 		   genflags,
568 		   NULL,
569 		   &localsocket,
570 		   NFS_VERSION, /* version 2 */
571 		   "udp",	/* XXX: shouldn't this be "udp"? */
572 		   &anh,
573 		   progpid_fs,	/* host name for kernel */
574 		   hostpid_fs); /* filesystem name for kernel */
575 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
576 
577   /*************************************************************************
578    * NOTE: while compute_nfs_args() works ok for regular NFS mounts	   *
579    * the toplvl one is not, and so some options must be corrected by hand  *
580    * more carefully, *after* compute_nfs_args() runs.			   *
581    *************************************************************************/
582   compute_automounter_nfs_args(&nfs_args, &mnt);
583 
584 /*
585  * For some reason, this mount may have to be done in the background, if I am
586  * using -D daemon.  I suspect that the actual act of mounting requires
587  * calling to hlfsd itself to invoke one or more of its nfs calls, to stat
588  * /mail.  That means that even if you say -D daemon, at least the mount
589  * of hlfsd itself on top of /mail will be done in the background.
590  * The other alternative I have is to run svc_run, but set a special
591  * signal handler to perform the mount in N seconds via some alarm.
592  *      -Erez Zadok.
593  */
594   if (!amuDebug(D_DAEMON)) {	/* Normal case */
595     plog(XLOG_INFO, "normal NFS mounting hlfsd service points");
596     if (mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 0, NULL, mnttab_file_name, 0) < 0)
597       fatal("nfsmount: %m");
598   } else {			/* asked for -D daemon */
599     if (fork() == 0) {		/* child runs mount */
600       am_set_mypid();
601       foreground = 0;
602       plog(XLOG_INFO, "child NFS mounting hlfsd service points");
603       if (mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 0, NULL, mnttab_file_name, 0) < 0) {
604 	fatal("nfsmount: %m");
605       }
606       exit(0);			/* all went well */
607     } else { /* fork failed or parent running */
608       plog(XLOG_INFO, "parent waiting 1sec for mount...");
609     }
610   }
611 
612 #ifdef HAVE_TRANSPORT_TYPE_TLI
613   /*
614    * XXX: this free_knetconfig() was not done for hlfsd before,
615    * and apparently there was a reason for it, but why? -Erez
616    */
617   free_knetconfig(nfs_args.knconf);
618   /*
619    * local automounter mounts do not allocate a special address, so
620    * no need to XFREE(nfs_args.addr) under TLI.
621    */
622 #endif /* HAVE_TRANSPORT_TYPE_TLI */
623 
624   if (printpid)
625     printf("%d\n", masterpid);
626 
627   plog(XLOG_INFO, "hlfsd ready to serve");
628   /*
629    * If asked not to fork a daemon (-D nodaemon), then hlfsd_init()
630    * will not run svc_run.  We must start svc_run here.
631    */
632   if (!amuDebug(D_DAEMON)) {
633     plog(XLOG_DEBUG, "starting no-daemon debugging svc_run");
634     svc_run();
635   }
636 
637   cleanup(0);			/* should never happen here */
638   return (0);			/* everything went fine? */
639 }
640 
641 
642 static void
643 hlfsd_init(void)
644 {
645   int child = 0;
646 #ifdef HAVE_SIGACTION
647   struct sigaction sa;
648 #endif /* HAVE_SIGACTION */
649 
650   /*
651    * Initialize file handles.
652    */
653   plog(XLOG_INFO, "initializing hlfsd file handles");
654   hlfsd_init_filehandles();
655 
656   /*
657    * If -D daemon then we must fork.
658    */
659   if (amuDebug(D_DAEMON))
660     child = fork();
661 
662   if (child < 0)
663     fatal("fork: %m");
664 
665   if (child != 0) {		/* parent process - save child pid */
666     masterpid = child;
667     am_set_mypid();		/* for logging routines */
668     return;
669   }
670 
671   /*
672    * CHILD CODE:
673    * initialize server
674    */
675 
676   plog(XLOG_INFO, "initializing home directory database");
677   plt_init();			/* initialize database */
678   plog(XLOG_INFO, "home directory database initialized");
679 
680   masterpid = serverpid = am_set_mypid(); /* for logging routines */
681 
682   /*
683    * SIGALRM/SIGHUP: reload password database if timer expired
684    * or user sent HUP signal.
685    */
686 #ifdef HAVE_SIGACTION
687   sa.sa_handler = reload;
688   sa.sa_flags = 0;
689   sigemptyset(&(sa.sa_mask));
690   sigaddset(&(sa.sa_mask), SIGALRM);
691   sigaddset(&(sa.sa_mask), SIGHUP);
692   sigaction(SIGALRM, &sa, NULL);
693   sigaction(SIGHUP, &sa, NULL);
694 #else /* not HAVE_SIGACTION */
695   signal(SIGALRM, reload);
696   signal(SIGHUP, reload);
697 #endif /* not HAVE_SIGACTION */
698 
699   /*
700    * SIGTERM: cleanup and exit.
701    */
702 #ifdef HAVE_SIGACTION
703   sa.sa_handler = cleanup;
704   sa.sa_flags = 0;
705   sigemptyset(&(sa.sa_mask));
706   sigaddset(&(sa.sa_mask), SIGTERM);
707   sigaction(SIGTERM, &sa, NULL);
708 #else /* not HAVE_SIGACTION */
709   signal(SIGTERM, cleanup);
710 #endif /* not HAVE_SIGACTION */
711 
712   /*
713    * SIGCHLD: interlock synchronization and testing
714    */
715 #ifdef HAVE_SIGACTION
716   sa.sa_handler = interlock;
717   sa.sa_flags = 0;
718   sigemptyset(&(sa.sa_mask));
719   sigaddset(&(sa.sa_mask), SIGCHLD);
720   sigaction(SIGCHLD, &sa, NULL);
721 #else /* not HAVE_SIGACTION */
722   signal(SIGCHLD, interlock);
723 #endif /* not HAVE_SIGACTION */
724 
725   /*
726    * SIGUSR1: dump internal hlfsd maps/cache to file
727    */
728 #ifdef HAVE_SIGACTION
729 # if defined(DEBUG) || defined(DEBUG_PRINT)
730   sa.sa_handler = plt_print;
731 # else /* not defined(DEBUG) || defined(DEBUG_PRINT) */
732   sa.sa_handler = SIG_IGN;
733 # endif /* not defined(DEBUG) || defined(DEBUG_PRINT) */
734   sa.sa_flags = 0;
735   sigemptyset(&(sa.sa_mask));
736   sigaddset(&(sa.sa_mask), SIGUSR1);
737   sigaction(SIGUSR1, &sa, NULL);
738 #else /* not HAVE_SIGACTION */
739 # if defined(DEBUG) || defined(DEBUG_PRINT)
740   signal(SIGUSR1, plt_print);
741 # else /* not defined(DEBUG) || defined(DEBUG_PRINT) */
742   signal(SIGUSR1, SIG_IGN);
743 # endif /* not defined(DEBUG) || defined(DEBUG_PRINT) */
744 #endif /* not HAVE_SIGACTION */
745 
746   if (setitimer(ITIMER_REAL, &reloadinterval, (struct itimerval *) NULL) < 0)
747     fatal("setitimer: %m");
748 
749   clocktime(&startup);
750 
751   /*
752    * If -D daemon, then start serving here in the child,
753    * and the parent will exit.  But if -D nodaemon, then
754    * skip this code and make sure svc_run is entered elsewhere.
755    */
756   if (amuDebug(D_DAEMON)) {
757     /*
758      * Dissociate from the controlling terminal
759      */
760     amu_release_controlling_tty();
761 
762     /*
763      * signal parent we are ready. parent should
764      * mount(2) and die.
765      */
766     if (kill(getppid(), SIGUSR2) < 0)
767       fatal("kill: %m");
768     plog(XLOG_INFO, "starting svc_run");
769     svc_run();
770     cleanup(0);		/* should never happen, just in case */
771   }
772 
773 }
774 
775 
776 static RETSIGTYPE
777 proceed(int signum)
778 {
779   stoplight = signum;
780 }
781 
782 
783 static RETSIGTYPE
784 reload(int signum)
785 {
786   int child;
787   int status;
788 
789   if (getpid() != masterpid)
790     return;
791 
792   /*
793    * If received a SIGHUP, close and reopen the log file (so that it
794    * can be rotated)
795    */
796   if (signum == SIGHUP && logfile)
797     switch_to_logfile(logfile, orig_umask, 0);
798 
799   /*
800    * parent performs the reload, while the child continues to serve
801    * clients accessing the home dir link.
802    */
803   if ((child = fork()) > 0) {
804     serverpid = child;		/* parent runs here */
805     am_set_mypid();
806 
807     plt_init();
808 
809     if (kill(child, SIGKILL) < 0) {
810       plog(XLOG_ERROR, "kill child: %m");
811     } else {			/* wait for child to die before continue */
812       if (wait(&status) != child) {
813 	/*
814 	 * I took out this line because it generates annoying output.  It
815 	 * indicates a very small bug in hlfsd which is totally harmless.
816 	 * It causes hlfsd to work a bit harder than it should.
817 	 * Nevertheless, I intend on fixing it in a future release.
818 	 * -Erez Zadok <ezk@cs.columbia.edu>
819 	 */
820 	/* plog(XLOG_ERROR, "unknown child"); */
821       }
822     }
823     serverpid = masterpid;
824   } else if (child < 0) {
825     plog(XLOG_ERROR, "unable to fork: %m");
826   } else {
827     /* let child handle requests while we reload */
828     serverpid = getpid();
829     am_set_mypid();
830   }
831 }
832 
833 
834 RETSIGTYPE
835 cleanup(int signum)
836 {
837   struct stat stbuf;
838   int umount_result;
839 
840   if (amuDebug(D_DAEMON)) {
841     if (getpid() != masterpid)
842       return;
843 
844     if (fork() != 0) {
845       masterpid = 0;
846       am_set_mypid();
847       return;
848     }
849   }
850   am_set_mypid();
851 
852   for (;;) {
853     while ((umount_result = UMOUNT_FS(dir_name, mnttab_file_name, 0)) == EBUSY) {
854       dlog("cleanup(): umount delaying for 10 seconds");
855       sleep(10);
856     }
857     if (stat(dir_name, &stbuf) == 0 && stbuf.st_ino == ROOTID) {
858       plog(XLOG_ERROR, "unable to unmount %s", dir_name);
859       plog(XLOG_ERROR, "suspending, unmount before terminating");
860       kill(am_mypid, SIGSTOP);
861       continue;			/* retry unmount */
862     }
863     break;
864   }
865 
866   if (amuDebug(D_DAEMON)) {
867     plog(XLOG_INFO, "cleanup(): killing processes and terminating");
868     kill(masterpid, SIGKILL);
869     kill(serverpid, SIGKILL);
870   }
871 
872   plog(XLOG_INFO, "hlfsd terminating with status 0\n");
873   _exit(0);
874 }
875 
876 
877 static RETSIGTYPE
878 reaper(int signum)
879 {
880   int result;
881 
882   if (wait(&result) == masterpid) {
883     _exit(4);
884   }
885 }
886 
887 
888 void
889 hlfsd_going_down(int rc)
890 {
891   int mypid = getpid();		/* XXX: should this be the global am_mypid */
892 
893   if (mypid == masterpid)
894     cleanup(0);
895   else if (mypid == serverpid)
896     kill(masterpid, SIGTERM);
897 
898   exit(rc);
899 }
900 
901 
902 void
903 fatal(char *mess)
904 {
905   if (logfile && !STREQ(logfile, "stderr")) {
906     char lessmess[128];
907     int messlen;
908 
909     messlen = strlen(mess);
910 
911     if (!STREQ(&mess[messlen + 1 - sizeof(ERRM)], ERRM))
912       fprintf(stderr, "%s: %s\n", am_get_progname(), mess);
913     else {
914       xstrlcpy(lessmess, mess, sizeof(lessmess));
915       lessmess[messlen - 4] = '\0';
916 
917       fprintf(stderr, "%s: %s: %s\n",
918 	      am_get_progname(), lessmess, strerror(errno));
919     }
920   }
921   plog(XLOG_FATAL, "%s", mess);
922 
923   hlfsd_going_down(1);
924 }
925