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