1/*
2 * Copyright (c) 1997-2014 Erez Zadok
3 * Copyright (c) 1989 Jan-Simon Pendry
4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1989 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *
36 * File: am-utils/hlfsd/hlfsd.c
37 *
38 * HLFSD was written at Columbia University Computer Science Department, by
39 * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
40 * It is being distributed under the same terms and conditions as amd does.
41 */
42
43#ifdef HAVE_CONFIG_H
44# include <config.h>
45#endif /* HAVE_CONFIG_H */
46#include <am_defs.h>
47#include <hlfsd.h>
48
49/*
50 * STATIC VARIABLES:
51 */
52static RETSIGTYPE proceed(int);
53static RETSIGTYPE reaper(int);
54static RETSIGTYPE reload(int);
55static char *hlfs_group = DEFAULT_HLFS_GROUP;
56static char default_dir_name[] = DEFAULT_DIRNAME;
57static char *dir_name = default_dir_name;
58static int printpid = 0;
59static int stoplight = 0;
60static void hlfsd_init(void);
61static void usage(void);
62
63static struct itimerval reloadinterval = {
64  {DEFAULT_INTERVAL, 0},
65  {DEFAULT_INTERVAL, 0}
66};
67
68/*
69 * default mount options.
70 */
71static char default_mntopts[] = "ro,noac";
72
73/*
74 * GLOBALS:
75 */
76SVCXPRT *nfsxprt;
77char *alt_spooldir = ALT_SPOOLDIR;
78char *home_subdir = HOME_SUBDIR;
79char *logfile = DEFAULT_LOGFILE;
80char *passwdfile = NULL;	/* alternate passwd file to use */
81char *slinkname = NULL;
82char hostname[MAXHOSTNAMELEN + 1] = "localhost";
83u_int cache_interval = DEFAULT_CACHE_INTERVAL;
84gid_t hlfs_gid = (gid_t) INVALIDID;
85int masterpid = 0;
86int noverify = 0;
87int orig_umask = 022;
88int serverpid = 0;
89nfstime startup;
90u_short nfs_port;
91
92/* symbol must be available always */
93#ifdef MNTTAB_FILE_NAME
94char *mnttab_file_name = MNTTAB_FILE_NAME;
95#else /* not MNTTAB_FILE_NAME */
96char *mnttab_file_name = NULL;
97#endif /* not MNTTAB_FILE_NAME */
98
99/* forward declarations */
100void hlfsd_going_down(int rc);
101void fatalerror(char *str);
102
103
104static void
105usage(void)
106{
107  fprintf(stderr,
108	  "Usage: %s [-Cfhnpv] [-a altdir] [-c cache-interval] [-g group]\n",
109	  am_get_progname());
110  fprintf(stderr, "\t[-i interval] [-l logfile] [-o mntopts] [-P passwdfile]\n");
111  show_opts('x', xlog_opt);
112#ifdef DEBUG
113  show_opts('D', dbg_opt);
114#endif /* DEBUG */
115  fprintf(stderr, "\t[dir_name [subdir]]\n");
116  exit(2);
117}
118
119
120void
121fatalerror(char *str)
122{
123#define ERRM ": %m"
124  size_t l = strlen(str) + sizeof(ERRM) - 1;
125  char *tmp = strnsave(str, l);
126  xstrlcat(tmp, ERRM, l);
127  fatal(tmp);
128}
129
130
131int
132main(int argc, char *argv[])
133{
134  char *dot;
135  char *mntopts = (char *) NULL;
136  char hostpid_fs[MAXHOSTNAMELEN + 1 + 16];	/* room for ":(pid###)" */
137  char progpid_fs[PROGNAMESZ + 1 + 11];		/* room for ":pid" */
138  char preopts[128];
139  char *progname;
140  int forcecache = 0;
141  int forcefast = 0;
142  int genflags = 0;
143  int opt, ret;
144  int opterrs = 0;
145  int retry;
146  int soNFS;			/* NFS socket */
147  int s = -99;
148  mntent_t mnt;
149  nfs_args_t nfs_args;
150  am_nfs_handle_t anh;
151  struct dirent *direntry;
152  struct group *grp;
153  struct stat stmodes;
154  DIR *mountdir;
155  MTYPE_TYPE type = MOUNT_TYPE_NFS;
156
157#ifdef HAVE_SIGACTION
158  struct sigaction sa;
159#endif /* not HAVE_SIGACTION */
160
161#ifndef HAVE_TRANSPORT_TYPE_TLI
162  struct sockaddr_in localsocket;
163#endif /* not HAVE_TRANSPORT_TYPE_TLI */
164
165
166  /* get program name and truncate so we don't overflow progpid_fs */
167
168  if ((progname = strrchr(argv[0], '/')) != NULL)
169    progname++;
170  else
171    progname = argv[0];
172  if ((int) strlen(progname) > PROGNAMESZ) /* truncate to reasonable size */
173    progname[PROGNAMESZ] = '\0';
174  am_set_progname(progname);
175
176  while ((opt = getopt(argc, argv, "a:c:CD:fg:hi:l:no:pP:x:v")) != -1)
177    switch (opt) {
178
179    case 'a':
180      if (!optarg || optarg[0] != '/') {
181	printf("%s: invalid directory for -a: %s\n",
182	       am_get_progname(), optarg);
183	exit(3);
184      }
185      alt_spooldir = optarg;
186      break;
187
188    case 'c':
189      if (!atoi(optarg)) {
190	printf("%s: invalid interval for -c: %s\n",
191	       am_get_progname(), optarg);
192	exit(3);
193      }
194      cache_interval = atoi(optarg);
195      break;
196
197    case 'C':
198      forcecache++;
199      break;
200
201    case 'f':
202      forcefast++;
203      break;
204
205    case 'g':
206      hlfs_group = optarg;
207      break;
208
209    case 'i':
210      if (!atoi(optarg)) {
211	printf("%s: invalid interval for -i: %s\n",
212	       am_get_progname(), optarg);
213	exit(3);
214      }
215      reloadinterval.it_interval.tv_sec = atoi(optarg);
216      reloadinterval.it_value.tv_sec = atoi(optarg);
217      break;
218
219    case 'l':
220      logfile = optarg;
221      break;
222
223    case 'n':
224      noverify++;
225      break;
226
227    case 'o':
228      mntopts = optarg;
229      break;
230
231    case 'p':
232      printpid++;
233      break;
234
235    case 'P':
236      passwdfile = optarg;
237      break;
238
239    case 'v':
240      fprintf(stderr, "%s\n", HLFSD_VERSION);
241      exit(0);
242
243    case 'x':
244      opterrs += switch_option(optarg);
245      break;
246
247    case 'D':
248#ifdef DEBUG
249      opterrs += debug_option(optarg);
250#else /* not DEBUG */
251      fprintf(stderr, "%s: not compiled with DEBUG -- sorry.\n", am_get_progname());
252#endif /* not DEBUG */
253      break;
254
255    case 'h':
256    case '?':
257      opterrs++;
258    }
259
260  /* need my pid before any dlog/plog */
261  am_set_mypid();
262#ifdef DEBUG
263  switch_option("debug");
264#endif /* DEBUG */
265
266/*
267 * Terminate if did not ask to forcecache (-C) and hlfsd would not be able
268 * to set the minimum cache intervals.
269 */
270#if !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_NFS_ARGS_T_ACREGMIN)
271  if (!forcecache) {
272    fprintf(stderr, "%s: will not be able to turn off attribute caches.\n", am_get_progname());
273    exit(1);
274  }
275#endif /* !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_NFS_ARGS_T_ACREGMIN) */
276
277
278  switch (argc - optind) {
279  case 2:
280    home_subdir = argv[optind + 1];
281  case 1:
282    dir_name = argv[optind];
283  case 0:
284    break;
285  default:
286    opterrs++;
287  }
288
289  if (opterrs)
290    usage();
291
292  /* ensure that only root can run hlfsd */
293  if (geteuid()) {
294    fprintf(stderr, "hlfsd can only be run as root\n");
295    exit(1);
296  }
297  setbuf(stdout, (char *) NULL);
298  umask(0);
299
300  /* find gid for hlfs_group */
301  if ((grp = getgrnam(hlfs_group)) == (struct group *) NULL) {
302    fprintf(stderr, "%s: cannot get gid for group \"%s\".\n",
303	    am_get_progname(), hlfs_group);
304  } else {
305    hlfs_gid = grp->gr_gid;
306  }
307
308  /* get hostname for logging and open log before we reset umask */
309  if (gethostname(hostname, sizeof(hostname)) == -1) {
310    fprintf(stderr, "%s: gethostname failed \"%s\".\n",
311	    am_get_progname(), strerror(errno));
312    exit(1);
313  }
314  hostname[sizeof(hostname) - 1] = '\0';
315  if ((dot = strchr(hostname, '.')) != NULL)
316    *dot = '\0';
317  orig_umask = umask(0);
318  if (logfile)
319    switch_to_logfile(logfile, orig_umask, 0);
320
321#ifndef MOUNT_TABLE_ON_FILE
322  if (amuDebug(D_MTAB))
323    dlog("-D mtab option ignored");
324#endif /* not MOUNT_TABLE_ON_FILE */
325
326  /* avoid hanging on other NFS servers if started elsewhere */
327  if (chdir("/") < 0)
328    fatal("cannot chdir to /: %m");
329
330  if (geteuid() != 0)
331    fatal("must be root to mount filesystems");
332
333  /*
334   * dir_name must match "^(/.*)/([^/]+)$", and is split at last '/' with
335   * slinkname = `basename $dir_name` - requires dir_name be writable
336   */
337
338  if (dir_name[0] != '/'
339      || ((slinkname = strrchr(dir_name, '/')), *slinkname++ = '\0',
340	  (dir_name[0] == '\0' || slinkname[0] == '\0'))) {
341    if (slinkname)
342      *--slinkname = '/';
343    printf("%s: invalid mount directory/link %s\n",
344	   am_get_progname(), dir_name);
345    exit(3);
346  }
347
348  if (!forcefast) {
349    /* make sure mount point exists and is at least mode 555 */
350    if (stat(dir_name, &stmodes) < 0)
351      if (errno != ENOENT || mkdirs(dir_name, 0555) < 0
352	  || stat(dir_name, &stmodes) < 0)
353	fatalerror(dir_name);
354
355    if ((stmodes.st_mode & 0555) != 0555) {
356      fprintf(stderr, "%s: directory %s not read/executable\n",
357	      am_get_progname(), dir_name);
358      plog(XLOG_WARNING, "directory %s not read/executable",
359	   dir_name);
360    }
361
362    /* warn if extraneous stuff will be hidden by mount */
363    if ((mountdir = opendir(dir_name)) == NULL)
364      fatalerror(dir_name);
365
366    while ((direntry = readdir(mountdir)) != NULL) {
367      if (!NSTREQ(".", direntry->d_name, NAMLEN(direntry)) &&
368	  !NSTREQ("..", direntry->d_name, NAMLEN(direntry)) &&
369	  !NSTREQ(slinkname, direntry->d_name, NAMLEN(direntry)))
370	break;
371    }
372
373    if (direntry != NULL) {
374      fprintf(stderr, "%s: %s/%s will be hidden by mount\n",
375	      am_get_progname(), dir_name, direntry->d_name);
376      plog(XLOG_WARNING, "%s/%s will be hidden by mount\n",
377	   dir_name, direntry->d_name);
378    }
379    closedir(mountdir);
380
381    /* make sure alternate spool dir exists */
382    if ((errno = mkdirs(alt_spooldir, OPEN_SPOOLMODE))) {
383      fprintf(stderr, "%s: cannot create alternate dir ",
384	      am_get_progname());
385      perror(alt_spooldir);
386      plog(XLOG_ERROR, "cannot create alternate dir %s: %m",
387	   alt_spooldir);
388    }
389    chmod(alt_spooldir, OPEN_SPOOLMODE);
390
391    /* create failsafe link to alternate spool directory */
392    *(slinkname-1) = '/';	/* unsplit dir_name to include link */
393    if (lstat(dir_name, &stmodes) == 0 &&
394	(stmodes.st_mode & S_IFMT) != S_IFLNK) {
395      fprintf(stderr, "%s: failsafe %s not a symlink\n",
396	      am_get_progname(), dir_name);
397      plog(XLOG_WARNING, "failsafe %s not a symlink\n",
398	   dir_name);
399    } else {
400      unlink(dir_name);
401
402      if (symlink(alt_spooldir, dir_name) < 0) {
403	fprintf(stderr,
404		"%s: cannot create failsafe symlink %s -> ",
405		am_get_progname(), dir_name);
406	perror(alt_spooldir);
407	plog(XLOG_WARNING,
408	     "cannot create failsafe symlink %s -> %s: %m",
409	     dir_name, alt_spooldir);
410      }
411    }
412
413    *(slinkname-1) = '\0';	/* resplit dir_name */
414  } /* end of "if (!forcefast) {" */
415
416  /*
417   * Register hlfsd as an nfs service with the portmapper.
418   */
419  ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2,
420    NFS_VERSION);
421  if (ret != 0)
422    fatal("cannot create NFS service");
423
424#ifdef HAVE_SIGACTION
425  sa.sa_handler = proceed;
426  sa.sa_flags = 0;
427  sigemptyset(&(sa.sa_mask));
428  sigaddset(&(sa.sa_mask), SIGUSR2);
429  sigaction(SIGUSR2, &sa, NULL);
430#else /* not HAVE_SIGACTION */
431  signal(SIGUSR2, proceed);
432#endif /* not HAVE_SIGACTION */
433
434  plog(XLOG_INFO, "Initializing hlfsd...");
435  hlfsd_init();			/* start up child (forking) to run svc_run */
436
437#ifdef HAVE_SIGACTION
438  sa.sa_handler = reaper;
439  sa.sa_flags = 0;
440  sigemptyset(&(sa.sa_mask));
441  sigaddset(&(sa.sa_mask), SIGCHLD);
442  sigaction(SIGCHLD, &sa, NULL);
443#else /* not HAVE_SIGACTION */
444  signal(SIGCHLD, reaper);
445#endif /* not HAVE_SIGACTION */
446
447  /*
448   * In the parent, if -D nodaemon, we don't need to
449   * set this signal handler.
450   */
451  if (amuDebug(D_DAEMON)) {
452    s = -99;
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	s = sigsuspend(&mask);	/* wait for child to set up */
460      }
461#else /* not HAVE_SIGSUSPEND */
462      s = 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
639static void
640hlfsd_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
773static RETSIGTYPE
774proceed(int signum)
775{
776  stoplight = signum;
777}
778
779
780static RETSIGTYPE
781reload(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
831RETSIGTYPE
832cleanup(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
874static RETSIGTYPE
875reaper(int signum)
876{
877  int result;
878
879  if (wait(&result) == masterpid) {
880    _exit(4);
881  }
882}
883
884
885void
886hlfsd_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
899void
900fatal(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