138494Sobrien/*
2310490Scy * Copyright (c) 1997-2014 Erez Zadok
338494Sobrien * Copyright (c) 1989 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1989 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
19310490Scy * 3. Neither the name of the University nor the names of its contributors
2038494Sobrien *    may be used to endorse or promote products derived from this software
2138494Sobrien *    without specific prior written permission.
2238494Sobrien *
2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2638494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3338494Sobrien * SUCH DAMAGE.
3438494Sobrien *
3538494Sobrien *
36174313Sobrien * File: am-utils/hlfsd/hlfsd.c
3738494Sobrien *
3838494Sobrien * HLFSD was written at Columbia University Computer Science Department, by
3938494Sobrien * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
4038494Sobrien * It is being distributed under the same terms and conditions as amd does.
4138494Sobrien */
4238494Sobrien
4338494Sobrien#ifdef HAVE_CONFIG_H
4438494Sobrien# include <config.h>
4538494Sobrien#endif /* HAVE_CONFIG_H */
4638494Sobrien#include <am_defs.h>
4738494Sobrien#include <hlfsd.h>
4838494Sobrien
4938494Sobrien/*
5038494Sobrien * STATIC VARIABLES:
5138494Sobrien */
5238494Sobrienstatic RETSIGTYPE proceed(int);
5338494Sobrienstatic RETSIGTYPE reaper(int);
5438494Sobrienstatic RETSIGTYPE reload(int);
5538494Sobrienstatic char *hlfs_group = DEFAULT_HLFS_GROUP;
5638494Sobrienstatic char default_dir_name[] = DEFAULT_DIRNAME;
5738494Sobrienstatic char *dir_name = default_dir_name;
5838494Sobrienstatic int printpid = 0;
5938494Sobrienstatic int stoplight = 0;
6038494Sobrienstatic void hlfsd_init(void);
6138494Sobrienstatic void usage(void);
6238494Sobrien
6338494Sobrienstatic struct itimerval reloadinterval = {
6438494Sobrien  {DEFAULT_INTERVAL, 0},
6538494Sobrien  {DEFAULT_INTERVAL, 0}
6638494Sobrien};
6738494Sobrien
6838494Sobrien/*
6938494Sobrien * default mount options.
7038494Sobrien */
7138494Sobrienstatic char default_mntopts[] = "ro,noac";
7238494Sobrien
7338494Sobrien/*
7438494Sobrien * GLOBALS:
7538494Sobrien */
7638494SobrienSVCXPRT *nfsxprt;
7738494Sobrienchar *alt_spooldir = ALT_SPOOLDIR;
7838494Sobrienchar *home_subdir = HOME_SUBDIR;
7938494Sobrienchar *logfile = DEFAULT_LOGFILE;
8038494Sobrienchar *passwdfile = NULL;	/* alternate passwd file to use */
81310490Scychar *slinkname = NULL;
8238500Sobrienchar hostname[MAXHOSTNAMELEN + 1] = "localhost";
83174313Sobrienu_int cache_interval = DEFAULT_CACHE_INTERVAL;
8438494Sobriengid_t hlfs_gid = (gid_t) INVALIDID;
8538494Sobrienint masterpid = 0;
8638494Sobrienint noverify = 0;
8742633Sobrienint orig_umask = 022;
8838494Sobrienint serverpid = 0;
8938494Sobriennfstime startup;
9038494Sobrienu_short nfs_port;
9138494Sobrien
9238494Sobrien/* symbol must be available always */
9382804Sobrien#ifdef MNTTAB_FILE_NAME
9438494Sobrienchar *mnttab_file_name = MNTTAB_FILE_NAME;
9582804Sobrien#else /* not MNTTAB_FILE_NAME */
9638494Sobrienchar *mnttab_file_name = NULL;
9782804Sobrien#endif /* not MNTTAB_FILE_NAME */
9838494Sobrien
9938494Sobrien/* forward declarations */
10038494Sobrienvoid hlfsd_going_down(int rc);
101310490Scyvoid fatalerror(char *str);
10238494Sobrien
10338494Sobrien
10438494Sobrienstatic void
10538494Sobrienusage(void)
10638494Sobrien{
10738494Sobrien  fprintf(stderr,
10838494Sobrien	  "Usage: %s [-Cfhnpv] [-a altdir] [-c cache-interval] [-g group]\n",
10942633Sobrien	  am_get_progname());
11038494Sobrien  fprintf(stderr, "\t[-i interval] [-l logfile] [-o mntopts] [-P passwdfile]\n");
11138494Sobrien  show_opts('x', xlog_opt);
11238494Sobrien#ifdef DEBUG
11338494Sobrien  show_opts('D', dbg_opt);
11438494Sobrien#endif /* DEBUG */
11538494Sobrien  fprintf(stderr, "\t[dir_name [subdir]]\n");
11638494Sobrien  exit(2);
11738494Sobrien}
11838494Sobrien
11938494Sobrien
120174313Sobrienvoid
121174313Sobrienfatalerror(char *str)
122174313Sobrien{
123174313Sobrien#define ERRM ": %m"
124174313Sobrien  size_t l = strlen(str) + sizeof(ERRM) - 1;
125174313Sobrien  char *tmp = strnsave(str, l);
126174313Sobrien  xstrlcat(tmp, ERRM, l);
127174313Sobrien  fatal(tmp);
128174313Sobrien}
129174313Sobrien
130174313Sobrien
13138494Sobrienint
13238494Sobrienmain(int argc, char *argv[])
13338494Sobrien{
13438494Sobrien  char *dot;
13538494Sobrien  char *mntopts = (char *) NULL;
13638494Sobrien  char hostpid_fs[MAXHOSTNAMELEN + 1 + 16];	/* room for ":(pid###)" */
13738494Sobrien  char progpid_fs[PROGNAMESZ + 1 + 11];		/* room for ":pid" */
13838494Sobrien  char preopts[128];
13942633Sobrien  char *progname;
14038494Sobrien  int forcecache = 0;
14138494Sobrien  int forcefast = 0;
14238494Sobrien  int genflags = 0;
14338494Sobrien  int opt, ret;
14438494Sobrien  int opterrs = 0;
14538494Sobrien  int retry;
14638494Sobrien  int soNFS;			/* NFS socket */
14738494Sobrien  int s = -99;
14838494Sobrien  mntent_t mnt;
14938494Sobrien  nfs_args_t nfs_args;
15038494Sobrien  am_nfs_handle_t anh;
15138494Sobrien  struct dirent *direntry;
15238494Sobrien  struct group *grp;
15338494Sobrien  struct stat stmodes;
15438494Sobrien  DIR *mountdir;
15538494Sobrien  MTYPE_TYPE type = MOUNT_TYPE_NFS;
15638494Sobrien
15738494Sobrien#ifdef HAVE_SIGACTION
15838494Sobrien  struct sigaction sa;
15938494Sobrien#endif /* not HAVE_SIGACTION */
16038494Sobrien
16138494Sobrien#ifndef HAVE_TRANSPORT_TYPE_TLI
16238494Sobrien  struct sockaddr_in localsocket;
16338494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
16438494Sobrien
16538494Sobrien
16638494Sobrien  /* get program name and truncate so we don't overflow progpid_fs */
16738494Sobrien
16838494Sobrien  if ((progname = strrchr(argv[0], '/')) != NULL)
16938494Sobrien    progname++;
17038494Sobrien  else
17138494Sobrien    progname = argv[0];
17238494Sobrien  if ((int) strlen(progname) > PROGNAMESZ) /* truncate to reasonable size */
17338494Sobrien    progname[PROGNAMESZ] = '\0';
17442633Sobrien  am_set_progname(progname);
17538494Sobrien
17642633Sobrien  while ((opt = getopt(argc, argv, "a:c:CD:fg:hi:l:no:pP:x:v")) != -1)
17738494Sobrien    switch (opt) {
17838494Sobrien
17938494Sobrien    case 'a':
18038494Sobrien      if (!optarg || optarg[0] != '/') {
18138494Sobrien	printf("%s: invalid directory for -a: %s\n",
18242633Sobrien	       am_get_progname(), optarg);
18338494Sobrien	exit(3);
18438494Sobrien      }
18538494Sobrien      alt_spooldir = optarg;
18638494Sobrien      break;
18738494Sobrien
18838494Sobrien    case 'c':
18938494Sobrien      if (!atoi(optarg)) {
19038494Sobrien	printf("%s: invalid interval for -c: %s\n",
19142633Sobrien	       am_get_progname(), optarg);
19238494Sobrien	exit(3);
19338494Sobrien      }
19438494Sobrien      cache_interval = atoi(optarg);
19538494Sobrien      break;
19638494Sobrien
19738494Sobrien    case 'C':
19838494Sobrien      forcecache++;
19938494Sobrien      break;
20038494Sobrien
20138494Sobrien    case 'f':
20238494Sobrien      forcefast++;
20338494Sobrien      break;
20438494Sobrien
20538494Sobrien    case 'g':
20638494Sobrien      hlfs_group = optarg;
20738494Sobrien      break;
20838494Sobrien
20938494Sobrien    case 'i':
21038494Sobrien      if (!atoi(optarg)) {
21138494Sobrien	printf("%s: invalid interval for -i: %s\n",
21242633Sobrien	       am_get_progname(), optarg);
21338494Sobrien	exit(3);
21438494Sobrien      }
21538494Sobrien      reloadinterval.it_interval.tv_sec = atoi(optarg);
21638494Sobrien      reloadinterval.it_value.tv_sec = atoi(optarg);
21738494Sobrien      break;
21838494Sobrien
21938494Sobrien    case 'l':
22038494Sobrien      logfile = optarg;
22138494Sobrien      break;
22238494Sobrien
22338494Sobrien    case 'n':
22438494Sobrien      noverify++;
22538494Sobrien      break;
22638494Sobrien
22738494Sobrien    case 'o':
22838494Sobrien      mntopts = optarg;
22938494Sobrien      break;
23038494Sobrien
23138494Sobrien    case 'p':
23238494Sobrien      printpid++;
23338494Sobrien      break;
23438494Sobrien
23538494Sobrien    case 'P':
23638494Sobrien      passwdfile = optarg;
23738494Sobrien      break;
23838494Sobrien
23938494Sobrien    case 'v':
24038494Sobrien      fprintf(stderr, "%s\n", HLFSD_VERSION);
24138494Sobrien      exit(0);
24238494Sobrien
24338494Sobrien    case 'x':
24438494Sobrien      opterrs += switch_option(optarg);
24538494Sobrien      break;
24638494Sobrien
24738494Sobrien    case 'D':
24838494Sobrien#ifdef DEBUG
24938494Sobrien      opterrs += debug_option(optarg);
25038494Sobrien#else /* not DEBUG */
25142633Sobrien      fprintf(stderr, "%s: not compiled with DEBUG -- sorry.\n", am_get_progname());
25238494Sobrien#endif /* not DEBUG */
25338494Sobrien      break;
25438494Sobrien
25538494Sobrien    case 'h':
25638494Sobrien    case '?':
25738494Sobrien      opterrs++;
25838494Sobrien    }
25938494Sobrien
26038494Sobrien  /* need my pid before any dlog/plog */
26142633Sobrien  am_set_mypid();
26238494Sobrien#ifdef DEBUG
26338494Sobrien  switch_option("debug");
26438494Sobrien#endif /* DEBUG */
26538494Sobrien
26638494Sobrien/*
26738494Sobrien * Terminate if did not ask to forcecache (-C) and hlfsd would not be able
26838494Sobrien * to set the minimum cache intervals.
26938494Sobrien */
270119682Smbr#if !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_NFS_ARGS_T_ACREGMIN)
27138494Sobrien  if (!forcecache) {
27242633Sobrien    fprintf(stderr, "%s: will not be able to turn off attribute caches.\n", am_get_progname());
27338494Sobrien    exit(1);
27438494Sobrien  }
275119682Smbr#endif /* !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_NFS_ARGS_T_ACREGMIN) */
27638494Sobrien
27738494Sobrien
27838494Sobrien  switch (argc - optind) {
27938494Sobrien  case 2:
28038494Sobrien    home_subdir = argv[optind + 1];
28138494Sobrien  case 1:
28238494Sobrien    dir_name = argv[optind];
28338494Sobrien  case 0:
28438494Sobrien    break;
28538494Sobrien  default:
28638494Sobrien    opterrs++;
28738494Sobrien  }
28838494Sobrien
28938494Sobrien  if (opterrs)
29038494Sobrien    usage();
29138494Sobrien
29238494Sobrien  /* ensure that only root can run hlfsd */
29338494Sobrien  if (geteuid()) {
29438494Sobrien    fprintf(stderr, "hlfsd can only be run as root\n");
29538494Sobrien    exit(1);
29638494Sobrien  }
29738494Sobrien  setbuf(stdout, (char *) NULL);
29838494Sobrien  umask(0);
29938494Sobrien
30038494Sobrien  /* find gid for hlfs_group */
30138494Sobrien  if ((grp = getgrnam(hlfs_group)) == (struct group *) NULL) {
30238494Sobrien    fprintf(stderr, "%s: cannot get gid for group \"%s\".\n",
30342633Sobrien	    am_get_progname(), hlfs_group);
30438494Sobrien  } else {
30538494Sobrien    hlfs_gid = grp->gr_gid;
30638494Sobrien  }
30738494Sobrien
30838494Sobrien  /* get hostname for logging and open log before we reset umask */
309310490Scy  if (gethostname(hostname, sizeof(hostname)) == -1) {
310310490Scy    fprintf(stderr, "%s: gethostname failed \"%s\".\n",
311310490Scy	    am_get_progname(), strerror(errno));
312310490Scy    exit(1);
313310490Scy  }
31438500Sobrien  hostname[sizeof(hostname) - 1] = '\0';
31538494Sobrien  if ((dot = strchr(hostname, '.')) != NULL)
31638494Sobrien    *dot = '\0';
31742633Sobrien  orig_umask = umask(0);
31838494Sobrien  if (logfile)
319174313Sobrien    switch_to_logfile(logfile, orig_umask, 0);
32038494Sobrien
321174313Sobrien#ifndef MOUNT_TABLE_ON_FILE
322174313Sobrien  if (amuDebug(D_MTAB))
32338494Sobrien    dlog("-D mtab option ignored");
324174313Sobrien#endif /* not MOUNT_TABLE_ON_FILE */
32538494Sobrien
32638494Sobrien  /* avoid hanging on other NFS servers if started elsewhere */
32738494Sobrien  if (chdir("/") < 0)
32838494Sobrien    fatal("cannot chdir to /: %m");
32938494Sobrien
33038494Sobrien  if (geteuid() != 0)
33138494Sobrien    fatal("must be root to mount filesystems");
33238494Sobrien
33338494Sobrien  /*
33438494Sobrien   * dir_name must match "^(/.*)/([^/]+)$", and is split at last '/' with
33538494Sobrien   * slinkname = `basename $dir_name` - requires dir_name be writable
33638494Sobrien   */
33738494Sobrien
33838494Sobrien  if (dir_name[0] != '/'
33938494Sobrien      || ((slinkname = strrchr(dir_name, '/')), *slinkname++ = '\0',
34038494Sobrien	  (dir_name[0] == '\0' || slinkname[0] == '\0'))) {
34138494Sobrien    if (slinkname)
34238494Sobrien      *--slinkname = '/';
34338494Sobrien    printf("%s: invalid mount directory/link %s\n",
34442633Sobrien	   am_get_progname(), dir_name);
34538494Sobrien    exit(3);
34638494Sobrien  }
34738494Sobrien
34838494Sobrien  if (!forcefast) {
34938494Sobrien    /* make sure mount point exists and is at least mode 555 */
35038494Sobrien    if (stat(dir_name, &stmodes) < 0)
35138494Sobrien      if (errno != ENOENT || mkdirs(dir_name, 0555) < 0
35238494Sobrien	  || stat(dir_name, &stmodes) < 0)
35338494Sobrien	fatalerror(dir_name);
35438494Sobrien
35538494Sobrien    if ((stmodes.st_mode & 0555) != 0555) {
35638494Sobrien      fprintf(stderr, "%s: directory %s not read/executable\n",
35742633Sobrien	      am_get_progname(), dir_name);
35838494Sobrien      plog(XLOG_WARNING, "directory %s not read/executable",
35938494Sobrien	   dir_name);
36038494Sobrien    }
36138494Sobrien
36238494Sobrien    /* warn if extraneous stuff will be hidden by mount */
36338494Sobrien    if ((mountdir = opendir(dir_name)) == NULL)
36438494Sobrien      fatalerror(dir_name);
36538494Sobrien
36638494Sobrien    while ((direntry = readdir(mountdir)) != NULL) {
36738494Sobrien      if (!NSTREQ(".", direntry->d_name, NAMLEN(direntry)) &&
36838494Sobrien	  !NSTREQ("..", direntry->d_name, NAMLEN(direntry)) &&
36938494Sobrien	  !NSTREQ(slinkname, direntry->d_name, NAMLEN(direntry)))
37038494Sobrien	break;
37138494Sobrien    }
37238494Sobrien
37338494Sobrien    if (direntry != NULL) {
37438494Sobrien      fprintf(stderr, "%s: %s/%s will be hidden by mount\n",
37542633Sobrien	      am_get_progname(), dir_name, direntry->d_name);
37638494Sobrien      plog(XLOG_WARNING, "%s/%s will be hidden by mount\n",
37738494Sobrien	   dir_name, direntry->d_name);
37838494Sobrien    }
37938494Sobrien    closedir(mountdir);
38038494Sobrien
38138494Sobrien    /* make sure alternate spool dir exists */
38238494Sobrien    if ((errno = mkdirs(alt_spooldir, OPEN_SPOOLMODE))) {
38338494Sobrien      fprintf(stderr, "%s: cannot create alternate dir ",
38442633Sobrien	      am_get_progname());
38538494Sobrien      perror(alt_spooldir);
38638494Sobrien      plog(XLOG_ERROR, "cannot create alternate dir %s: %m",
38738494Sobrien	   alt_spooldir);
38838494Sobrien    }
38938494Sobrien    chmod(alt_spooldir, OPEN_SPOOLMODE);
39038494Sobrien
39138494Sobrien    /* create failsafe link to alternate spool directory */
392174313Sobrien    *(slinkname-1) = '/';	/* unsplit dir_name to include link */
39338494Sobrien    if (lstat(dir_name, &stmodes) == 0 &&
39438494Sobrien	(stmodes.st_mode & S_IFMT) != S_IFLNK) {
39538494Sobrien      fprintf(stderr, "%s: failsafe %s not a symlink\n",
39642633Sobrien	      am_get_progname(), dir_name);
39738494Sobrien      plog(XLOG_WARNING, "failsafe %s not a symlink\n",
39838494Sobrien	   dir_name);
39938494Sobrien    } else {
40038494Sobrien      unlink(dir_name);
40138494Sobrien
40238494Sobrien      if (symlink(alt_spooldir, dir_name) < 0) {
40338494Sobrien	fprintf(stderr,
40438494Sobrien		"%s: cannot create failsafe symlink %s -> ",
40542633Sobrien		am_get_progname(), dir_name);
40638494Sobrien	perror(alt_spooldir);
40738494Sobrien	plog(XLOG_WARNING,
40838494Sobrien	     "cannot create failsafe symlink %s -> %s: %m",
40938494Sobrien	     dir_name, alt_spooldir);
41038494Sobrien      }
41138494Sobrien    }
41238494Sobrien
413174313Sobrien    *(slinkname-1) = '\0';	/* resplit dir_name */
41438494Sobrien  } /* end of "if (!forcefast) {" */
41538494Sobrien
41638494Sobrien  /*
41738494Sobrien   * Register hlfsd as an nfs service with the portmapper.
41838494Sobrien   */
419310490Scy  ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2,
420310490Scy    NFS_VERSION);
42138494Sobrien  if (ret != 0)
42238494Sobrien    fatal("cannot create NFS service");
42338494Sobrien
42438494Sobrien#ifdef HAVE_SIGACTION
42538494Sobrien  sa.sa_handler = proceed;
426174313Sobrien  sa.sa_flags = 0;
42738494Sobrien  sigemptyset(&(sa.sa_mask));
42838494Sobrien  sigaddset(&(sa.sa_mask), SIGUSR2);
42938494Sobrien  sigaction(SIGUSR2, &sa, NULL);
43038494Sobrien#else /* not HAVE_SIGACTION */
43138494Sobrien  signal(SIGUSR2, proceed);
43238494Sobrien#endif /* not HAVE_SIGACTION */
43338494Sobrien
43438494Sobrien  plog(XLOG_INFO, "Initializing hlfsd...");
43538494Sobrien  hlfsd_init();			/* start up child (forking) to run svc_run */
43638494Sobrien
43738494Sobrien#ifdef HAVE_SIGACTION
43838494Sobrien  sa.sa_handler = reaper;
439174313Sobrien  sa.sa_flags = 0;
44038494Sobrien  sigemptyset(&(sa.sa_mask));
44138494Sobrien  sigaddset(&(sa.sa_mask), SIGCHLD);
44238494Sobrien  sigaction(SIGCHLD, &sa, NULL);
44338494Sobrien#else /* not HAVE_SIGACTION */
44438494Sobrien  signal(SIGCHLD, reaper);
44538494Sobrien#endif /* not HAVE_SIGACTION */
44638494Sobrien
44738494Sobrien  /*
448310490Scy   * In the parent, if -D nodaemon, we don't need to
44938494Sobrien   * set this signal handler.
45038494Sobrien   */
451310490Scy  if (amuDebug(D_DAEMON)) {
45238494Sobrien    s = -99;
45338494Sobrien    while (stoplight != SIGUSR2) {
45438494Sobrien      plog(XLOG_INFO, "parent waits for child to setup (stoplight=%d)", stoplight);
455174313Sobrien#ifdef HAVE_SIGSUSPEND
456174313Sobrien      {
457174313Sobrien	sigset_t mask;
458174313Sobrien	sigemptyset(&mask);
459174313Sobrien	s = sigsuspend(&mask);	/* wait for child to set up */
460174313Sobrien      }
461174313Sobrien#else /* not HAVE_SIGSUSPEND */
46238494Sobrien      s = sigpause(0);		/* wait for child to set up */
463174313Sobrien#endif /* not HAVE_SIGSUSPEND */
46438494Sobrien      sleep(1);
46538494Sobrien    }
46638494Sobrien  }
46738494Sobrien
46838494Sobrien  /*
46938494Sobrien   * setup options to mount table (/etc/{mtab,mnttab}) entry
47038494Sobrien   */
471174313Sobrien  xsnprintf(hostpid_fs, sizeof(hostpid_fs),
472174313Sobrien	    "%s:(pid%d)", hostname, masterpid);
47338494Sobrien  memset((char *) &mnt, 0, sizeof(mnt));
47438494Sobrien  mnt.mnt_dir = dir_name;	/* i.e., "/mail" */
47538494Sobrien  mnt.mnt_fsname = hostpid_fs;
47638494Sobrien  if (mntopts) {
47738494Sobrien    mnt.mnt_opts = mntopts;
47838494Sobrien  } else {
479174313Sobrien    xstrlcpy(preopts, default_mntopts, sizeof(preopts));
48038494Sobrien    /*
48138494Sobrien     * Turn off all kinds of attribute and symlink caches as
48238494Sobrien     * much as possible.  Also make sure that mount does not
48338494Sobrien     * show up to df.
48438494Sobrien     */
48538494Sobrien#ifdef MNTTAB_OPT_INTR
486174313Sobrien    xstrlcat(preopts, ",", sizeof(preopts));
487174313Sobrien    xstrlcat(preopts, MNTTAB_OPT_INTR, sizeof(preopts));
48838494Sobrien#endif /* MNTTAB_OPT_INTR */
48938494Sobrien#ifdef MNTTAB_OPT_IGNORE
490174313Sobrien    xstrlcat(preopts, ",", sizeof(preopts));
491174313Sobrien    xstrlcat(preopts, MNTTAB_OPT_IGNORE, sizeof(preopts));
49238494Sobrien#endif /* MNTTAB_OPT_IGNORE */
49338494Sobrien#ifdef MNT2_GEN_OPT_CACHE
494174313Sobrien    xstrlcat(preopts, ",nocache", sizeof(preopts));
49538494Sobrien#endif /* MNT2_GEN_OPT_CACHE */
49638494Sobrien#ifdef MNT2_NFS_OPT_SYMTTL
497174313Sobrien    xstrlcat(preopts, ",symttl=0", sizeof(preopts));
49838494Sobrien#endif /* MNT2_NFS_OPT_SYMTTL */
49938494Sobrien    mnt.mnt_opts = preopts;
50038494Sobrien  }
50138494Sobrien
50238494Sobrien  /*
50338494Sobrien   * Make sure that amd's top-level NFS mounts are hidden by default
50438494Sobrien   * from df.
50538494Sobrien   * If they don't appear to support the either the "ignore" mnttab
50638494Sobrien   * option entry, or the "auto" one, set the mount type to "nfs".
50738494Sobrien   */
508174313Sobrien#ifdef HIDE_MOUNT_TYPE
50938494Sobrien  mnt.mnt_type = HIDE_MOUNT_TYPE;
510174313Sobrien#else /* not HIDE_MOUNT_TYPE */
511174313Sobrien  mnt.mnt_type = "nfs";
512174313Sobrien#endif /* not HIDE_MOUNT_TYPE */
51338494Sobrien  /* some systems don't have a mount type, but a mount flag */
51438494Sobrien
51538494Sobrien#ifndef HAVE_TRANSPORT_TYPE_TLI
516174313Sobrien  amu_get_myaddress(&localsocket.sin_addr, NULL);
51738494Sobrien  localsocket.sin_family = AF_INET;
51838494Sobrien  localsocket.sin_port = htons(nfsxprt->xp_port);
51938494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
52038494Sobrien
52138494Sobrien  /*
52238494Sobrien   * Update hostname field.
52338494Sobrien   * Make some name prog:pid (i.e., hlfsd:174) for hostname
52438494Sobrien   */
525174313Sobrien  xsnprintf(progpid_fs, sizeof(progpid_fs),
526174313Sobrien	    "%s:%d", am_get_progname(), masterpid);
52738494Sobrien
52838494Sobrien  /* Most kernels have a name length restriction. */
52938494Sobrien  if ((int) strlen(progpid_fs) >= (int) MAXHOSTNAMELEN)
530174313Sobrien    xstrlcpy(progpid_fs + MAXHOSTNAMELEN - 3, "..",
531174313Sobrien	     sizeof(progpid_fs) - MAXHOSTNAMELEN + 3);
53238494Sobrien
53338494Sobrien  genflags = compute_mount_flags(&mnt);
53438494Sobrien
53538494Sobrien  retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
53638494Sobrien  if (retry <= 0)
53738494Sobrien    retry = 1;			/* XXX */
53838494Sobrien
539174313Sobrien  memmove(&anh.v2, root_fhp, sizeof(*root_fhp));
54038494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
54138494Sobrien  compute_nfs_args(&nfs_args,
54238494Sobrien		   &mnt,
54338494Sobrien		   genflags,
54438494Sobrien		   nfsncp,
54538494Sobrien		   NULL,	/* remote host IP addr is set below */
54638494Sobrien		   NFS_VERSION,	/* version 2 */
54738494Sobrien		   "udp",	/* XXX: shouldn't this be "udp"? */
54838494Sobrien		   &anh,
54938494Sobrien		   progpid_fs,	/* host name for kernel */
55038494Sobrien		   hostpid_fs); /* filesystem name for kernel */
55138494Sobrien  /*
55238494Sobrien   * IMPORTANT: set the correct IP address AFTERWARDS.  It cannot
55338494Sobrien   * be done using the normal mechanism of compute_nfs_args(), because
55438494Sobrien   * that one will allocate a new address and use NFS_SA_DREF() to copy
55538494Sobrien   * parts to it, while assuming that the ip_addr passed is always
55638494Sobrien   * a "struct sockaddr_in".  That assumption is incorrect on TLI systems,
55738494Sobrien   * because they define a special macro HOST_SELF which is DIFFERENT
55838494Sobrien   * than localhost (127.0.0.1)!
55938494Sobrien   */
56038494Sobrien  nfs_args.addr = &nfsxprt->xp_ltaddr;
56138494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */
56238494Sobrien  compute_nfs_args(&nfs_args,
56338494Sobrien		   &mnt,
56438494Sobrien		   genflags,
565174313Sobrien		   NULL,
56638494Sobrien		   &localsocket,
56738494Sobrien		   NFS_VERSION, /* version 2 */
56838494Sobrien		   "udp",	/* XXX: shouldn't this be "udp"? */
56938494Sobrien		   &anh,
57038494Sobrien		   progpid_fs,	/* host name for kernel */
57138494Sobrien		   hostpid_fs); /* filesystem name for kernel */
57238494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
57338494Sobrien
57438494Sobrien  /*************************************************************************
57538494Sobrien   * NOTE: while compute_nfs_args() works ok for regular NFS mounts	   *
57638494Sobrien   * the toplvl one is not, and so some options must be corrected by hand  *
57738494Sobrien   * more carefully, *after* compute_nfs_args() runs.			   *
57838494Sobrien   *************************************************************************/
57938494Sobrien  compute_automounter_nfs_args(&nfs_args, &mnt);
58038494Sobrien
58138494Sobrien/*
58238494Sobrien * For some reason, this mount may have to be done in the background, if I am
583174313Sobrien * using -D daemon.  I suspect that the actual act of mounting requires
58438494Sobrien * calling to hlfsd itself to invoke one or more of its nfs calls, to stat
585174313Sobrien * /mail.  That means that even if you say -D daemon, at least the mount
58638494Sobrien * of hlfsd itself on top of /mail will be done in the background.
58738494Sobrien * The other alternative I have is to run svc_run, but set a special
58838494Sobrien * signal handler to perform the mount in N seconds via some alarm.
58938494Sobrien *      -Erez Zadok.
59038494Sobrien */
591174313Sobrien  if (!amuDebug(D_DAEMON)) {	/* Normal case */
592174313Sobrien    plog(XLOG_INFO, "normal NFS mounting hlfsd service points");
593174313Sobrien    if (mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 0, NULL, mnttab_file_name, 0) < 0)
59438494Sobrien      fatal("nfsmount: %m");
595174313Sobrien  } else {			/* asked for -D daemon */
59638494Sobrien    if (fork() == 0) {		/* child runs mount */
59742633Sobrien      am_set_mypid();
59838494Sobrien      foreground = 0;
59938494Sobrien      plog(XLOG_INFO, "child NFS mounting hlfsd service points");
600174313Sobrien      if (mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 0, NULL, mnttab_file_name, 0) < 0) {
60138494Sobrien	fatal("nfsmount: %m");
60238494Sobrien      }
60338494Sobrien      exit(0);			/* all went well */
60438494Sobrien    } else { /* fork failed or parent running */
60538494Sobrien      plog(XLOG_INFO, "parent waiting 1sec for mount...");
60638494Sobrien    }
60738494Sobrien  }
60838494Sobrien
60938494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
61038494Sobrien  /*
61138494Sobrien   * XXX: this free_knetconfig() was not done for hlfsd before,
61238494Sobrien   * and apparently there was a reason for it, but why? -Erez
61338494Sobrien   */
61438494Sobrien  free_knetconfig(nfs_args.knconf);
61538494Sobrien  /*
61638494Sobrien   * local automounter mounts do not allocate a special address, so
61738494Sobrien   * no need to XFREE(nfs_args.addr) under TLI.
61838494Sobrien   */
61938494Sobrien#endif /* HAVE_TRANSPORT_TYPE_TLI */
62038494Sobrien
62138494Sobrien  if (printpid)
62238494Sobrien    printf("%d\n", masterpid);
62338494Sobrien
62438494Sobrien  plog(XLOG_INFO, "hlfsd ready to serve");
62538494Sobrien  /*
626310490Scy   * If asked not to fork a daemon (-D nodaemon), then hlfsd_init()
62738494Sobrien   * will not run svc_run.  We must start svc_run here.
62838494Sobrien   */
629310490Scy  if (!amuDebug(D_DAEMON)) {
630174313Sobrien    plog(XLOG_DEBUG, "starting no-daemon debugging svc_run");
63138494Sobrien    svc_run();
632174313Sobrien  }
63338494Sobrien
63438494Sobrien  cleanup(0);			/* should never happen here */
63538494Sobrien  return (0);			/* everything went fine? */
63638494Sobrien}
63738494Sobrien
63838494Sobrien
63938494Sobrienstatic void
64038494Sobrienhlfsd_init(void)
64138494Sobrien{
64238494Sobrien  int child = 0;
64338494Sobrien#ifdef HAVE_SIGACTION
64438494Sobrien  struct sigaction sa;
64538494Sobrien#endif /* HAVE_SIGACTION */
64638494Sobrien
64738494Sobrien  /*
64838494Sobrien   * Initialize file handles.
64938494Sobrien   */
65038494Sobrien  plog(XLOG_INFO, "initializing hlfsd file handles");
65138494Sobrien  hlfsd_init_filehandles();
65238494Sobrien
65338494Sobrien  /*
654310490Scy   * If -D daemon then we must fork.
65538494Sobrien   */
656310490Scy  if (amuDebug(D_DAEMON))
65738494Sobrien    child = fork();
65838494Sobrien
65938494Sobrien  if (child < 0)
66038494Sobrien    fatal("fork: %m");
66138494Sobrien
66238494Sobrien  if (child != 0) {		/* parent process - save child pid */
66338494Sobrien    masterpid = child;
66442633Sobrien    am_set_mypid();		/* for logging routines */
66538494Sobrien    return;
66638494Sobrien  }
66738494Sobrien
66838494Sobrien  /*
66938494Sobrien   * CHILD CODE:
67038494Sobrien   * initialize server
67138494Sobrien   */
67238494Sobrien
67338494Sobrien  plog(XLOG_INFO, "initializing home directory database");
67438494Sobrien  plt_init();			/* initialize database */
67538494Sobrien  plog(XLOG_INFO, "home directory database initialized");
67638494Sobrien
67742633Sobrien  masterpid = serverpid = am_set_mypid(); /* for logging routines */
67838494Sobrien
67938494Sobrien  /*
68038494Sobrien   * SIGALRM/SIGHUP: reload password database if timer expired
68138494Sobrien   * or user sent HUP signal.
68238494Sobrien   */
68338494Sobrien#ifdef HAVE_SIGACTION
68438494Sobrien  sa.sa_handler = reload;
685174313Sobrien  sa.sa_flags = 0;
68638494Sobrien  sigemptyset(&(sa.sa_mask));
68738494Sobrien  sigaddset(&(sa.sa_mask), SIGALRM);
68838494Sobrien  sigaddset(&(sa.sa_mask), SIGHUP);
68938494Sobrien  sigaction(SIGALRM, &sa, NULL);
69038494Sobrien  sigaction(SIGHUP, &sa, NULL);
69138494Sobrien#else /* not HAVE_SIGACTION */
69238494Sobrien  signal(SIGALRM, reload);
69338494Sobrien  signal(SIGHUP, reload);
69438494Sobrien#endif /* not HAVE_SIGACTION */
69538494Sobrien
69638494Sobrien  /*
69738494Sobrien   * SIGTERM: cleanup and exit.
69838494Sobrien   */
69938494Sobrien#ifdef HAVE_SIGACTION
70038494Sobrien  sa.sa_handler = cleanup;
701174313Sobrien  sa.sa_flags = 0;
70238494Sobrien  sigemptyset(&(sa.sa_mask));
70338494Sobrien  sigaddset(&(sa.sa_mask), SIGTERM);
70438494Sobrien  sigaction(SIGTERM, &sa, NULL);
70538494Sobrien#else /* not HAVE_SIGACTION */
70638494Sobrien  signal(SIGTERM, cleanup);
70738494Sobrien#endif /* not HAVE_SIGACTION */
70838494Sobrien
70938494Sobrien  /*
71042633Sobrien   * SIGCHLD: interlock synchronization and testing
71138494Sobrien   */
71238494Sobrien#ifdef HAVE_SIGACTION
71338494Sobrien  sa.sa_handler = interlock;
714174313Sobrien  sa.sa_flags = 0;
71538494Sobrien  sigemptyset(&(sa.sa_mask));
71638494Sobrien  sigaddset(&(sa.sa_mask), SIGCHLD);
71738494Sobrien  sigaction(SIGCHLD, &sa, NULL);
71838494Sobrien#else /* not HAVE_SIGACTION */
71938494Sobrien  signal(SIGCHLD, interlock);
72038494Sobrien#endif /* not HAVE_SIGACTION */
72138494Sobrien
72238494Sobrien  /*
72338494Sobrien   * SIGUSR1: dump internal hlfsd maps/cache to file
72438494Sobrien   */
72538494Sobrien#ifdef HAVE_SIGACTION
72638494Sobrien# if defined(DEBUG) || defined(DEBUG_PRINT)
72738494Sobrien  sa.sa_handler = plt_print;
72838494Sobrien# else /* not defined(DEBUG) || defined(DEBUG_PRINT) */
72938494Sobrien  sa.sa_handler = SIG_IGN;
73038494Sobrien# endif /* not defined(DEBUG) || defined(DEBUG_PRINT) */
731174313Sobrien  sa.sa_flags = 0;
73238494Sobrien  sigemptyset(&(sa.sa_mask));
73338494Sobrien  sigaddset(&(sa.sa_mask), SIGUSR1);
73438494Sobrien  sigaction(SIGUSR1, &sa, NULL);
73538494Sobrien#else /* not HAVE_SIGACTION */
73638494Sobrien# if defined(DEBUG) || defined(DEBUG_PRINT)
73738494Sobrien  signal(SIGUSR1, plt_print);
73838494Sobrien# else /* not defined(DEBUG) || defined(DEBUG_PRINT) */
73938494Sobrien  signal(SIGUSR1, SIG_IGN);
74038494Sobrien# endif /* not defined(DEBUG) || defined(DEBUG_PRINT) */
74138494Sobrien#endif /* not HAVE_SIGACTION */
74238494Sobrien
743310490Scy  if (setitimer(ITIMER_REAL, &reloadinterval, (struct itimerval *) NULL) < 0)
74438494Sobrien    fatal("setitimer: %m");
74538494Sobrien
746174313Sobrien  clocktime(&startup);
74738494Sobrien
74838494Sobrien  /*
749310490Scy   * If -D daemon, then start serving here in the child,
750310490Scy   * and the parent will exit.  But if -D nodaemon, then
75138494Sobrien   * skip this code and make sure svc_run is entered elsewhere.
75238494Sobrien   */
753310490Scy  if (amuDebug(D_DAEMON)) {
75438494Sobrien    /*
75538494Sobrien     * Dissociate from the controlling terminal
75638494Sobrien     */
75738494Sobrien    amu_release_controlling_tty();
75838494Sobrien
75938494Sobrien    /*
76038494Sobrien     * signal parent we are ready. parent should
76138494Sobrien     * mount(2) and die.
76238494Sobrien     */
76338494Sobrien    if (kill(getppid(), SIGUSR2) < 0)
76438494Sobrien      fatal("kill: %m");
76538494Sobrien    plog(XLOG_INFO, "starting svc_run");
76638494Sobrien    svc_run();
76738494Sobrien    cleanup(0);		/* should never happen, just in case */
768174313Sobrien  }
76938494Sobrien
77038494Sobrien}
77138494Sobrien
77238494Sobrien
77338494Sobrienstatic RETSIGTYPE
77438494Sobrienproceed(int signum)
77538494Sobrien{
77638494Sobrien  stoplight = signum;
77738494Sobrien}
77838494Sobrien
77938494Sobrien
78038494Sobrienstatic RETSIGTYPE
78138494Sobrienreload(int signum)
78238494Sobrien{
78338494Sobrien  int child;
78438494Sobrien  int status;
78538494Sobrien
78638494Sobrien  if (getpid() != masterpid)
78738494Sobrien    return;
78838494Sobrien
78938494Sobrien  /*
79038494Sobrien   * If received a SIGHUP, close and reopen the log file (so that it
79138494Sobrien   * can be rotated)
79238494Sobrien   */
79338494Sobrien  if (signum == SIGHUP && logfile)
794174313Sobrien    switch_to_logfile(logfile, orig_umask, 0);
79538494Sobrien
79638494Sobrien  /*
79738494Sobrien   * parent performs the reload, while the child continues to serve
79838494Sobrien   * clients accessing the home dir link.
79938494Sobrien   */
80038494Sobrien  if ((child = fork()) > 0) {
80138494Sobrien    serverpid = child;		/* parent runs here */
80242633Sobrien    am_set_mypid();
80338494Sobrien
80438494Sobrien    plt_init();
80538494Sobrien
80638494Sobrien    if (kill(child, SIGKILL) < 0) {
80738494Sobrien      plog(XLOG_ERROR, "kill child: %m");
80838494Sobrien    } else {			/* wait for child to die before continue */
80938494Sobrien      if (wait(&status) != child) {
81038494Sobrien	/*
81138494Sobrien	 * I took out this line because it generates annoying output.  It
81238494Sobrien	 * indicates a very small bug in hlfsd which is totally harmless.
81338494Sobrien	 * It causes hlfsd to work a bit harder than it should.
81438494Sobrien	 * Nevertheless, I intend on fixing it in a future release.
81538494Sobrien	 * -Erez Zadok <ezk@cs.columbia.edu>
81638494Sobrien	 */
81738494Sobrien	/* plog(XLOG_ERROR, "unknown child"); */
81838494Sobrien      }
81938494Sobrien    }
82038494Sobrien    serverpid = masterpid;
82138494Sobrien  } else if (child < 0) {
82238494Sobrien    plog(XLOG_ERROR, "unable to fork: %m");
82338494Sobrien  } else {
82438494Sobrien    /* let child handle requests while we reload */
82538494Sobrien    serverpid = getpid();
82642633Sobrien    am_set_mypid();
82738494Sobrien  }
82838494Sobrien}
82938494Sobrien
83038494Sobrien
83138494SobrienRETSIGTYPE
83238494Sobriencleanup(int signum)
83338494Sobrien{
83438494Sobrien  struct stat stbuf;
83538494Sobrien  int umount_result;
83638494Sobrien
837310490Scy  if (amuDebug(D_DAEMON)) {
83838494Sobrien    if (getpid() != masterpid)
839119682Smbr      return;
84038494Sobrien
84138494Sobrien    if (fork() != 0) {
842119682Smbr      masterpid = 0;
843119682Smbr      am_set_mypid();
844119682Smbr      return;
845119682Smbr    }
846174313Sobrien  }
84742633Sobrien  am_set_mypid();
84838494Sobrien
84938494Sobrien  for (;;) {
850174313Sobrien    while ((umount_result = UMOUNT_FS(dir_name, mnttab_file_name, 0)) == EBUSY) {
85138494Sobrien      dlog("cleanup(): umount delaying for 10 seconds");
85238494Sobrien      sleep(10);
85338494Sobrien    }
85438494Sobrien    if (stat(dir_name, &stbuf) == 0 && stbuf.st_ino == ROOTID) {
85538494Sobrien      plog(XLOG_ERROR, "unable to unmount %s", dir_name);
85638494Sobrien      plog(XLOG_ERROR, "suspending, unmount before terminating");
85742633Sobrien      kill(am_mypid, SIGSTOP);
85838494Sobrien      continue;			/* retry unmount */
85938494Sobrien    }
86038494Sobrien    break;
86138494Sobrien  }
86238494Sobrien
863310490Scy  if (amuDebug(D_DAEMON)) {
864174313Sobrien    plog(XLOG_INFO, "cleanup(): killing processes and terminating");
86538494Sobrien    kill(masterpid, SIGKILL);
86638494Sobrien    kill(serverpid, SIGKILL);
867174313Sobrien  }
86838494Sobrien
86938494Sobrien  plog(XLOG_INFO, "hlfsd terminating with status 0\n");
870174313Sobrien  _exit(0);
87138494Sobrien}
87238494Sobrien
87338494Sobrien
87438494Sobrienstatic RETSIGTYPE
87538494Sobrienreaper(int signum)
87638494Sobrien{
87738494Sobrien  int result;
87838494Sobrien
87938494Sobrien  if (wait(&result) == masterpid) {
880174313Sobrien    _exit(4);
88138494Sobrien  }
88238494Sobrien}
88338494Sobrien
88438494Sobrien
88538494Sobrienvoid
88638494Sobrienhlfsd_going_down(int rc)
88738494Sobrien{
88842633Sobrien  int mypid = getpid();		/* XXX: should this be the global am_mypid */
88938494Sobrien
89038494Sobrien  if (mypid == masterpid)
89138494Sobrien    cleanup(0);
89238494Sobrien  else if (mypid == serverpid)
89338494Sobrien    kill(masterpid, SIGTERM);
89438494Sobrien
89538494Sobrien  exit(rc);
89638494Sobrien}
89738494Sobrien
89838494Sobrien
89938494Sobrienvoid
90038494Sobrienfatal(char *mess)
90138494Sobrien{
90238494Sobrien  if (logfile && !STREQ(logfile, "stderr")) {
90338494Sobrien    char lessmess[128];
90438494Sobrien    int messlen;
90538494Sobrien
90638494Sobrien    messlen = strlen(mess);
90738494Sobrien
90838494Sobrien    if (!STREQ(&mess[messlen + 1 - sizeof(ERRM)], ERRM))
90942633Sobrien      fprintf(stderr, "%s: %s\n", am_get_progname(), mess);
91038494Sobrien    else {
911174313Sobrien      xstrlcpy(lessmess, mess, sizeof(lessmess));
91238494Sobrien      lessmess[messlen - 4] = '\0';
91338494Sobrien
914119682Smbr      fprintf(stderr, "%s: %s: %s\n",
915119682Smbr	      am_get_progname(), lessmess, strerror(errno));
91638494Sobrien    }
91738494Sobrien  }
91882804Sobrien  plog(XLOG_FATAL, "%s", mess);
91938494Sobrien
92038494Sobrien  hlfsd_going_down(1);
92138494Sobrien}
922