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