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