]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/amd/hlfsd/hlfsd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / amd / hlfsd / hlfsd.c
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  */
56 static RETSIGTYPE proceed(int);
57 static RETSIGTYPE reaper(int);
58 static RETSIGTYPE reload(int);
59 static char *hlfs_group = DEFAULT_HLFS_GROUP;
60 static char default_dir_name[] = DEFAULT_DIRNAME;
61 static char *dir_name = default_dir_name;
62 static int printpid = 0;
63 static int stoplight = 0;
64 static void hlfsd_init(void);
65 static void usage(void);
66
67 static struct itimerval reloadinterval = {
68   {DEFAULT_INTERVAL, 0},
69   {DEFAULT_INTERVAL, 0}
70 };
71
72 /*
73  * default mount options.
74  */
75 static char default_mntopts[] = "ro,noac";
76
77 /*
78  * GLOBALS:
79  */
80 SVCXPRT *nfsxprt;
81 char *alt_spooldir = ALT_SPOOLDIR;
82 char *home_subdir = HOME_SUBDIR;
83 char *logfile = DEFAULT_LOGFILE;
84 char *passwdfile = NULL;        /* alternate passwd file to use */
85 char *slinkname = 0;
86 char hostname[MAXHOSTNAMELEN + 1] = "localhost";
87 u_int cache_interval = DEFAULT_CACHE_INTERVAL;
88 gid_t hlfs_gid = (gid_t) INVALIDID;
89 int masterpid = 0;
90 int noverify = 0;
91 int orig_umask = 022;
92 int serverpid = 0;
93 nfstime startup;
94 u_short nfs_port;
95
96 /* symbol must be available always */
97 #ifdef MNTTAB_FILE_NAME
98 char *mnttab_file_name = MNTTAB_FILE_NAME;
99 #else /* not MNTTAB_FILE_NAME */
100 char *mnttab_file_name = NULL;
101 #endif /* not MNTTAB_FILE_NAME */
102
103 /* forward declarations */
104 void hlfsd_going_down(int rc);
105
106
107 static void
108 usage(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
123 void
124 fatalerror(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
134 int
135 main(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
644 static void
645 hlfsd_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
778 static RETSIGTYPE
779 proceed(int signum)
780 {
781   stoplight = signum;
782 }
783
784
785 static RETSIGTYPE
786 reload(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
836 RETSIGTYPE
837 cleanup(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
879 static RETSIGTYPE
880 reaper(int signum)
881 {
882   int result;
883
884   if (wait(&result) == masterpid) {
885     _exit(4);
886   }
887 }
888
889
890 void
891 hlfsd_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
904 void
905 fatal(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 }