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