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