1 /* $NetBSD: hlfsd.c,v 1.3 2015/01/17 17:46:31 christos Exp $ */
2
3 /*
4 * Copyright (c) 1997-2014 Erez Zadok
5 * Copyright (c) 1989 Jan-Simon Pendry
6 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1989 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 *
38 * File: am-utils/hlfsd/hlfsd.c
39 *
40 * HLFSD was written at Columbia University Computer Science Department, by
41 * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
42 * It is being distributed under the same terms and conditions as amd does.
43 */
44
45 #ifdef HAVE_CONFIG_H
46 # include <config.h>
47 #endif /* HAVE_CONFIG_H */
48 #include <am_defs.h>
49 #include <hlfsd.h>
50
51 /*
52 * STATIC VARIABLES:
53 */
54 static RETSIGTYPE proceed(int);
55 static RETSIGTYPE reaper(int);
56 static RETSIGTYPE reload(int);
57 static char *hlfs_group = DEFAULT_HLFS_GROUP;
58 static char default_dir_name[] = DEFAULT_DIRNAME;
59 static char *dir_name = default_dir_name;
60 static int printpid = 0;
61 static int stoplight = 0;
62 static void hlfsd_init(void);
63 static void usage(void);
64
65 static struct itimerval reloadinterval = {
66 {DEFAULT_INTERVAL, 0},
67 {DEFAULT_INTERVAL, 0}
68 };
69
70 /*
71 * default mount options.
72 */
73 static char default_mntopts[] = "ro,noac";
74
75 /*
76 * GLOBALS:
77 */
78 SVCXPRT *nfsxprt;
79 char *alt_spooldir = ALT_SPOOLDIR;
80 char *home_subdir = HOME_SUBDIR;
81 char *logfile = DEFAULT_LOGFILE;
82 char *passwdfile = NULL; /* alternate passwd file to use */
83 char *slinkname = NULL;
84 char hostname[MAXHOSTNAMELEN + 1] = "localhost";
85 u_int cache_interval = DEFAULT_CACHE_INTERVAL;
86 gid_t hlfs_gid = (gid_t) INVALIDID;
87 int masterpid = 0;
88 int noverify = 0;
89 int orig_umask = 022;
90 int serverpid = 0;
91 nfstime startup;
92 u_short nfs_port;
93
94 /* symbol must be available always */
95 #ifdef MNTTAB_FILE_NAME
96 char *mnttab_file_name = MNTTAB_FILE_NAME;
97 #else /* not MNTTAB_FILE_NAME */
98 char *mnttab_file_name = NULL;
99 #endif /* not MNTTAB_FILE_NAME */
100
101 /* forward declarations */
102 void hlfsd_going_down(int rc);
103 void fatalerror(char *str);
104
105
106 static void
usage(void)107 usage(void)
108 {
109 fprintf(stderr,
110 "Usage: %s [-Cfhnpv] [-a altdir] [-c cache-interval] [-g group]\n",
111 am_get_progname());
112 fprintf(stderr, "\t[-i interval] [-l logfile] [-o mntopts] [-P passwdfile]\n");
113 show_opts('x', xlog_opt);
114 #ifdef DEBUG
115 show_opts('D', dbg_opt);
116 #endif /* DEBUG */
117 fprintf(stderr, "\t[dir_name [subdir]]\n");
118 exit(2);
119 }
120
121
122 void
fatalerror(char * str)123 fatalerror(char *str)
124 {
125 #define ERRM ": %m"
126 size_t l = strlen(str) + sizeof(ERRM) - 1;
127 char *tmp = strnsave(str, l);
128 xstrlcat(tmp, ERRM, l);
129 fatal(tmp);
130 }
131
132
133 int
main(int argc,char * argv[])134 main(int argc, char *argv[])
135 {
136 char *dot;
137 char *mntopts = (char *) NULL;
138 char hostpid_fs[MAXHOSTNAMELEN + 1 + 16]; /* room for ":(pid###)" */
139 char progpid_fs[PROGNAMESZ + 1 + 11]; /* room for ":pid" */
140 char preopts[128];
141 char *progname;
142 int forcecache = 0;
143 int forcefast = 0;
144 int genflags = 0;
145 int opt, ret;
146 int opterrs = 0;
147 int retry;
148 int soNFS; /* NFS socket */
149 mntent_t mnt;
150 nfs_args_t nfs_args;
151 am_nfs_handle_t anh;
152 struct dirent *direntry;
153 struct group *grp;
154 struct stat stmodes;
155 DIR *mountdir;
156 MTYPE_TYPE type = MOUNT_TYPE_NFS;
157
158 #ifdef HAVE_SIGACTION
159 struct sigaction sa;
160 #endif /* not HAVE_SIGACTION */
161
162 #ifndef HAVE_TRANSPORT_TYPE_TLI
163 struct sockaddr_in localsocket;
164 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
165
166
167 /* get program name and truncate so we don't overflow progpid_fs */
168
169 if ((progname = strrchr(argv[0], '/')) != NULL)
170 progname++;
171 else
172 progname = argv[0];
173 if ((int) strlen(progname) > PROGNAMESZ) /* truncate to reasonable size */
174 progname[PROGNAMESZ] = '\0';
175 am_set_progname(progname);
176
177 while ((opt = getopt(argc, argv, "a:c:CD:fg:hi:l:no:pP:x:v")) != -1)
178 switch (opt) {
179
180 case 'a':
181 if (!optarg || optarg[0] != '/') {
182 printf("%s: invalid directory for -a: %s\n",
183 am_get_progname(), optarg);
184 exit(3);
185 }
186 alt_spooldir = optarg;
187 break;
188
189 case 'c':
190 if (!atoi(optarg)) {
191 printf("%s: invalid interval for -c: %s\n",
192 am_get_progname(), optarg);
193 exit(3);
194 }
195 cache_interval = atoi(optarg);
196 break;
197
198 case 'C':
199 forcecache++;
200 break;
201
202 case 'f':
203 forcefast++;
204 break;
205
206 case 'g':
207 hlfs_group = optarg;
208 break;
209
210 case 'i':
211 if (!atoi(optarg)) {
212 printf("%s: invalid interval for -i: %s\n",
213 am_get_progname(), optarg);
214 exit(3);
215 }
216 reloadinterval.it_interval.tv_sec = atoi(optarg);
217 reloadinterval.it_value.tv_sec = atoi(optarg);
218 break;
219
220 case 'l':
221 logfile = optarg;
222 break;
223
224 case 'n':
225 noverify++;
226 break;
227
228 case 'o':
229 mntopts = optarg;
230 break;
231
232 case 'p':
233 printpid++;
234 break;
235
236 case 'P':
237 passwdfile = optarg;
238 break;
239
240 case 'v':
241 fprintf(stderr, "%s\n", HLFSD_VERSION);
242 exit(0);
243
244 case 'x':
245 opterrs += switch_option(optarg);
246 break;
247
248 case 'D':
249 #ifdef DEBUG
250 opterrs += debug_option(optarg);
251 #else /* not DEBUG */
252 fprintf(stderr, "%s: not compiled with DEBUG -- sorry.\n", am_get_progname());
253 #endif /* not DEBUG */
254 break;
255
256 case 'h':
257 case '?':
258 opterrs++;
259 }
260
261 /* need my pid before any dlog/plog */
262 am_set_mypid();
263 #ifdef DEBUG
264 switch_option("debug");
265 #endif /* DEBUG */
266
267 /*
268 * Terminate if did not ask to forcecache (-C) and hlfsd would not be able
269 * to set the minimum cache intervals.
270 */
271 #if !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_NFS_ARGS_T_ACREGMIN)
272 if (!forcecache) {
273 fprintf(stderr, "%s: will not be able to turn off attribute caches.\n", am_get_progname());
274 exit(1);
275 }
276 #endif /* !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_NFS_ARGS_T_ACREGMIN) */
277
278
279 switch (argc - optind) {
280 case 2:
281 home_subdir = argv[optind + 1];
282 case 1:
283 dir_name = argv[optind];
284 case 0:
285 break;
286 default:
287 opterrs++;
288 }
289
290 if (opterrs)
291 usage();
292
293 /* ensure that only root can run hlfsd */
294 if (geteuid()) {
295 fprintf(stderr, "hlfsd can only be run as root\n");
296 exit(1);
297 }
298 setbuf(stdout, (char *) NULL);
299 umask(0);
300
301 /* find gid for hlfs_group */
302 if ((grp = getgrnam(hlfs_group)) == (struct group *) NULL) {
303 fprintf(stderr, "%s: cannot get gid for group \"%s\".\n",
304 am_get_progname(), hlfs_group);
305 } else {
306 hlfs_gid = grp->gr_gid;
307 }
308
309 /* get hostname for logging and open log before we reset umask */
310 if (gethostname(hostname, sizeof(hostname)) == -1) {
311 fprintf(stderr, "%s: gethostname failed \"%s\".\n",
312 am_get_progname(), strerror(errno));
313 exit(1);
314 }
315 hostname[sizeof(hostname) - 1] = '\0';
316 if ((dot = strchr(hostname, '.')) != NULL)
317 *dot = '\0';
318 orig_umask = umask(0);
319 if (logfile)
320 switch_to_logfile(logfile, orig_umask, 0);
321
322 #ifndef MOUNT_TABLE_ON_FILE
323 if (amuDebug(D_MTAB))
324 dlog("-D mtab option ignored");
325 #endif /* not MOUNT_TABLE_ON_FILE */
326
327 /* avoid hanging on other NFS servers if started elsewhere */
328 if (chdir("/") < 0)
329 fatal("cannot chdir to /: %m");
330
331 if (geteuid() != 0)
332 fatal("must be root to mount filesystems");
333
334 /*
335 * dir_name must match "^(/.*)/([^/]+)$", and is split at last '/' with
336 * slinkname = `basename $dir_name` - requires dir_name be writable
337 */
338
339 if (dir_name[0] != '/'
340 || ((slinkname = strrchr(dir_name, '/')), *slinkname++ = '\0',
341 (dir_name[0] == '\0' || slinkname[0] == '\0'))) {
342 if (slinkname)
343 *--slinkname = '/';
344 printf("%s: invalid mount directory/link %s\n",
345 am_get_progname(), dir_name);
346 exit(3);
347 }
348
349 if (!forcefast) {
350 /* make sure mount point exists and is at least mode 555 */
351 if (stat(dir_name, &stmodes) < 0)
352 if (errno != ENOENT || mkdirs(dir_name, 0555) < 0
353 || stat(dir_name, &stmodes) < 0)
354 fatalerror(dir_name);
355
356 if ((stmodes.st_mode & 0555) != 0555) {
357 fprintf(stderr, "%s: directory %s not read/executable\n",
358 am_get_progname(), dir_name);
359 plog(XLOG_WARNING, "directory %s not read/executable",
360 dir_name);
361 }
362
363 /* warn if extraneous stuff will be hidden by mount */
364 if ((mountdir = opendir(dir_name)) == NULL)
365 fatalerror(dir_name);
366
367 while ((direntry = readdir(mountdir)) != NULL) {
368 if (!NSTREQ(".", direntry->d_name, NAMLEN(direntry)) &&
369 !NSTREQ("..", direntry->d_name, NAMLEN(direntry)) &&
370 !NSTREQ(slinkname, direntry->d_name, NAMLEN(direntry)))
371 break;
372 }
373
374 if (direntry != NULL) {
375 fprintf(stderr, "%s: %s/%s will be hidden by mount\n",
376 am_get_progname(), dir_name, direntry->d_name);
377 plog(XLOG_WARNING, "%s/%s will be hidden by mount\n",
378 dir_name, direntry->d_name);
379 }
380 closedir(mountdir);
381
382 /* make sure alternate spool dir exists */
383 if ((errno = mkdirs(alt_spooldir, OPEN_SPOOLMODE))) {
384 fprintf(stderr, "%s: cannot create alternate dir ",
385 am_get_progname());
386 perror(alt_spooldir);
387 plog(XLOG_ERROR, "cannot create alternate dir %s: %m",
388 alt_spooldir);
389 }
390 chmod(alt_spooldir, OPEN_SPOOLMODE);
391
392 /* create failsafe link to alternate spool directory */
393 *(slinkname-1) = '/'; /* unsplit dir_name to include link */
394 if (lstat(dir_name, &stmodes) == 0 &&
395 (stmodes.st_mode & S_IFMT) != S_IFLNK) {
396 fprintf(stderr, "%s: failsafe %s not a symlink\n",
397 am_get_progname(), dir_name);
398 plog(XLOG_WARNING, "failsafe %s not a symlink\n",
399 dir_name);
400 } else {
401 unlink(dir_name);
402
403 if (symlink(alt_spooldir, dir_name) < 0) {
404 fprintf(stderr,
405 "%s: cannot create failsafe symlink %s -> ",
406 am_get_progname(), dir_name);
407 perror(alt_spooldir);
408 plog(XLOG_WARNING,
409 "cannot create failsafe symlink %s -> %s: %m",
410 dir_name, alt_spooldir);
411 }
412 }
413
414 *(slinkname-1) = '\0'; /* resplit dir_name */
415 } /* end of "if (!forcefast) {" */
416
417 /*
418 * Register hlfsd as an nfs service with the portmapper.
419 */
420 ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2,
421 NFS_VERSION);
422 if (ret != 0)
423 fatal("cannot create NFS service");
424
425 #ifdef HAVE_SIGACTION
426 sa.sa_handler = proceed;
427 sa.sa_flags = 0;
428 sigemptyset(&(sa.sa_mask));
429 sigaddset(&(sa.sa_mask), SIGUSR2);
430 sigaction(SIGUSR2, &sa, NULL);
431 #else /* not HAVE_SIGACTION */
432 signal(SIGUSR2, proceed);
433 #endif /* not HAVE_SIGACTION */
434
435 plog(XLOG_INFO, "Initializing hlfsd...");
436 hlfsd_init(); /* start up child (forking) to run svc_run */
437
438 #ifdef HAVE_SIGACTION
439 sa.sa_handler = reaper;
440 sa.sa_flags = 0;
441 sigemptyset(&(sa.sa_mask));
442 sigaddset(&(sa.sa_mask), SIGCHLD);
443 sigaction(SIGCHLD, &sa, NULL);
444 #else /* not HAVE_SIGACTION */
445 signal(SIGCHLD, reaper);
446 #endif /* not HAVE_SIGACTION */
447
448 /*
449 * In the parent, if -D nodaemon, we don't need to
450 * set this signal handler.
451 */
452 if (amuDebug(D_DAEMON)) {
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 (void)sigsuspend(&mask); /* wait for child to set up */
460 }
461 #else /* not HAVE_SIGSUSPEND */
462 (void)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 <ezk@cs.columbia.edu>
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