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