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