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