1 /* $NetBSD: autil.c,v 1.1.1.2 2009/03/20 20:26:48 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2009 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 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. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgment: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * 42 * File: am-utils/amd/autil.c 43 * 44 */ 45 46 /* 47 * utilities specified to amd, taken out of the older amd/util.c. 48 */ 49 50 #ifdef HAVE_CONFIG_H 51 # include <config.h> 52 #endif /* HAVE_CONFIG_H */ 53 #include <am_defs.h> 54 #include <amd.h> 55 56 int NumChildren = 0; /* number of children of primary amd */ 57 static char invalid_keys[] = "\"'!;@ \t\n"; 58 59 /**************************************************************************** 60 *** MACROS *** 61 ****************************************************************************/ 62 63 #ifdef HAVE_TRANSPORT_TYPE_TLI 64 # define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */ 65 #endif /* HAVE_TRANSPORT_TYPE_TLI */ 66 67 68 /**************************************************************************** 69 *** FORWARD DEFINITIONS *** 70 ****************************************************************************/ 71 static void domain_strip(char *otherdom, char *localdom); 72 static int dofork(void); 73 74 75 /**************************************************************************** 76 *** FUNCTIONS *** 77 ****************************************************************************/ 78 79 /* 80 * Copy s into p, reallocating p if necessary 81 */ 82 char * 83 strealloc(char *p, char *s) 84 { 85 size_t len = strlen(s) + 1; 86 87 p = (char *) xrealloc((voidp) p, len); 88 89 xstrlcpy(p, s, len); 90 #ifdef DEBUG_MEM 91 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 92 malloc_verify(); 93 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 94 #endif /* DEBUG_MEM */ 95 return p; 96 } 97 98 99 /* 100 * Strip off the trailing part of a domain 101 * to produce a short-form domain relative 102 * to the local host domain. 103 * Note that this has no effect if the domain 104 * names do not have the same number of 105 * components. If that restriction proves 106 * to be a problem then the loop needs recoding 107 * to skip from right to left and do partial 108 * matches along the way -- ie more expensive. 109 */ 110 static void 111 domain_strip(char *otherdom, char *localdom) 112 { 113 char *p1, *p2; 114 115 if ((p1 = strchr(otherdom, '.')) && 116 (p2 = strchr(localdom, '.')) && 117 STREQ(p1 + 1, p2 + 1)) 118 *p1 = '\0'; 119 } 120 121 122 /* 123 * Normalize a host name: replace cnames with real names, and decide if to 124 * strip domain name or not. 125 */ 126 void 127 host_normalize(char **chp) 128 { 129 /* 130 * Normalize hosts is used to resolve host name aliases 131 * and replace them with the standard-form name. 132 * Invoked with "-n" command line option. 133 */ 134 if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) { 135 struct hostent *hp; 136 hp = gethostbyname(*chp); 137 if (hp && hp->h_addrtype == AF_INET) { 138 dlog("Hostname %s normalized to %s", *chp, hp->h_name); 139 *chp = strealloc(*chp, (char *) hp->h_name); 140 } 141 } 142 if (gopt.flags & CFM_DOMAIN_STRIP) { 143 domain_strip(*chp, hostd); 144 } 145 } 146 147 148 /* 149 * Keys are not allowed to contain " ' ! or ; to avoid 150 * problems with macro expansions. 151 */ 152 int 153 valid_key(char *key) 154 { 155 while (*key) 156 if (strchr(invalid_keys, *key++)) 157 return FALSE; 158 return TRUE; 159 } 160 161 162 void 163 forcibly_timeout_mp(am_node *mp) 164 { 165 mntfs *mf = mp->am_mnt; 166 /* 167 * Arrange to timeout this node 168 */ 169 if (mf && ((mp->am_flags & AMF_ROOT) || 170 (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) { 171 /* 172 * We aren't going to schedule a timeout, so we need to notify the 173 * child here unless we are already unmounting, in which case that 174 * process is responsible for notifying the child. 175 */ 176 if (mf->mf_flags & MFF_UNMOUNTING) 177 plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path); 178 else { 179 plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path); 180 notify_child(mp, AMQ_UMNT_FAILED, EBUSY, 0); 181 } 182 } else { 183 plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path); 184 mp->am_flags &= ~AMF_NOTIMEOUT; 185 mp->am_ttl = clocktime(NULL); 186 /* 187 * Force mtime update of parent dir, to prevent DNLC/dcache from caching 188 * the old entry, which could result in ESTALE errors, bad symlinks, and 189 * more. 190 */ 191 clocktime(&mp->am_parent->am_fattr.na_mtime); 192 reschedule_timeout_mp(); 193 } 194 } 195 196 197 void 198 mf_mounted(mntfs *mf, bool_t call_free_opts) 199 { 200 int quoted; 201 int wasmounted = mf->mf_flags & MFF_MOUNTED; 202 203 if (!wasmounted) { 204 /* 205 * If this is a freshly mounted 206 * filesystem then update the 207 * mntfs structure... 208 */ 209 mf->mf_flags |= MFF_MOUNTED; 210 mf->mf_error = 0; 211 212 /* 213 * Do mounted callback 214 */ 215 if (mf->mf_ops->mounted) 216 mf->mf_ops->mounted(mf); 217 218 /* 219 * Be careful when calling free_ops and XFREE here. Some pseudo file 220 * systems like nfsx call this function (mf_mounted), even though it 221 * would be called by the lower-level amd file system functions. nfsx 222 * needs to call this function because of the other actions it takes. 223 * So we pass a boolean from the caller (yes, not so clean workaround) 224 * to determine if we should free or not. If we're not freeing (often 225 * because we're called from a callback function), then just to be sure, 226 * we'll zero out the am_opts structure and set the pointer to NULL. 227 * The parent mntfs node owns this memory and is going to free it with a 228 * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code). 229 */ 230 if (call_free_opts) { 231 free_opts(mf->mf_fo); /* this free is needed to prevent leaks */ 232 XFREE(mf->mf_fo); /* (also this one) */ 233 } else { 234 memset(mf->mf_fo, 0, sizeof(am_opts)); 235 mf->mf_fo = NULL; 236 } 237 } 238 239 if (mf->mf_flags & MFF_RESTART) { 240 mf->mf_flags &= ~MFF_RESTART; 241 dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags); 242 } 243 244 /* 245 * Log message 246 */ 247 quoted = strchr(mf->mf_info, ' ') != 0; 248 plog(XLOG_INFO, "%s%s%s %s fstype %s on %s", 249 quoted ? "\"" : "", 250 mf->mf_info, 251 quoted ? "\"" : "", 252 wasmounted ? "referenced" : "mounted", 253 mf->mf_ops->fs_type, mf->mf_mount); 254 } 255 256 257 void 258 am_mounted(am_node *mp) 259 { 260 int notimeout = 0; /* assume normal timeouts initially */ 261 mntfs *mf = mp->am_mnt; 262 263 /* 264 * This is the parent mntfs which does the mf->mf_fo (am_opts type), and 265 * we're passing TRUE here to tell mf_mounted to actually free the 266 * am_opts. See a related comment in mf_mounted(). 267 */ 268 mf_mounted(mf, TRUE); 269 270 #ifdef HAVE_FS_AUTOFS 271 if (mf->mf_flags & MFF_IS_AUTOFS) 272 autofs_mounted(mp); 273 #endif /* HAVE_FS_AUTOFS */ 274 275 /* 276 * Patch up path for direct mounts 277 */ 278 if (mp->am_parent && mp->am_parent->am_mnt->mf_fsflags & FS_DIRECT) 279 mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); 280 281 /* 282 * Check whether this mount should be cached permanently or not, 283 * and handle user-requested timeouts. 284 */ 285 /* first check if file system was set to never timeout */ 286 if (mf->mf_fsflags & FS_NOTIMEOUT) 287 notimeout = 1; 288 /* next, alter that decision by map flags */ 289 if (mf->mf_mopts) { 290 mntent_t mnt; 291 mnt.mnt_opts = mf->mf_mopts; 292 293 /* umount option: user wants to unmount this entry */ 294 if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount")) 295 notimeout = 0; 296 /* noumount option: user does NOT want to unmount this entry */ 297 if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount")) 298 notimeout = 1; 299 /* utimeout=N option: user wants to unmount this option AND set timeout */ 300 if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) 301 mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */ 302 else 303 notimeout = 0; 304 /* special case: don't try to unmount "/" (it can never succeed) */ 305 if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0') 306 notimeout = 1; 307 } 308 /* finally set actual flags */ 309 if (notimeout) { 310 mp->am_flags |= AMF_NOTIMEOUT; 311 plog(XLOG_INFO, "%s set to never timeout", mp->am_path); 312 } else { 313 mp->am_flags &= ~AMF_NOTIMEOUT; 314 plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo); 315 } 316 317 /* 318 * If this node is a symlink then 319 * compute the length of the returned string. 320 */ 321 if (mp->am_fattr.na_type == NFLNK) 322 mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount); 323 324 /* 325 * Record mount time, and update am_stats at the same time. 326 */ 327 mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime); 328 new_ttl(mp); 329 330 /* 331 * Update mtime of parent node (copying "struct nfstime" in '=' below) 332 */ 333 if (mp->am_parent && mp->am_parent->am_mnt) 334 mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime; 335 336 /* 337 * This is ugly, but essentially unavoidable 338 * Sublinks must be treated separately as type==link 339 * when the base type is different. 340 */ 341 if (mp->am_link && mf->mf_ops != &amfs_link_ops) 342 amfs_link_ops.mount_fs(mp, mf); 343 344 /* 345 * Now, if we can, do a reply to our client here 346 * to speed things up. 347 */ 348 #ifdef HAVE_FS_AUTOFS 349 if (mp->am_flags & AMF_AUTOFS) 350 autofs_mount_succeeded(mp); 351 else 352 #endif /* HAVE_FS_AUTOFS */ 353 nfs_quick_reply(mp, 0); 354 355 /* 356 * Update stats 357 */ 358 amd_stats.d_mok++; 359 } 360 361 362 /* 363 * Replace mount point with a reference to an error filesystem. 364 * The mount point (struct mntfs) is NOT discarded, 365 * the caller must do it if it wants to _before_ calling this function. 366 */ 367 void 368 assign_error_mntfs(am_node *mp) 369 { 370 int error; 371 dlog("assign_error_mntfs"); 372 /* 373 * Save the old error code 374 */ 375 error = mp->am_error; 376 if (error <= 0) 377 error = mp->am_mnt->mf_error; 378 /* 379 * Allocate a new error reference 380 */ 381 mp->am_mnt = new_mntfs(); 382 /* 383 * Put back the error code 384 */ 385 mp->am_mnt->mf_error = error; 386 mp->am_mnt->mf_flags |= MFF_ERROR; 387 /* 388 * Zero the error in the mount point 389 */ 390 mp->am_error = 0; 391 } 392 393 394 /* 395 * Build a new map cache for this node, or re-use 396 * an existing cache for the same map. 397 */ 398 void 399 amfs_mkcacheref(mntfs *mf) 400 { 401 char *cache; 402 403 if (mf->mf_fo && mf->mf_fo->opt_cache) 404 cache = mf->mf_fo->opt_cache; 405 else 406 cache = "none"; 407 mf->mf_private = (opaque_t) mapc_find(mf->mf_info, 408 cache, 409 (mf->mf_fo ? mf->mf_fo->opt_maptype : NULL), 410 mf->mf_mount); 411 mf->mf_prfree = mapc_free; 412 } 413 414 415 /* 416 * Locate next node in sibling list which is mounted 417 * and is not an error node. 418 */ 419 am_node * 420 next_nonerror_node(am_node *xp) 421 { 422 mntfs *mf; 423 424 /* 425 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> 426 * Fixes a race condition when mounting direct automounts. 427 * Also fixes a problem when doing a readdir on a directory 428 * containing hung automounts. 429 */ 430 while (xp && 431 (!(mf = xp->am_mnt) || /* No mounted filesystem */ 432 mf->mf_error != 0 || /* There was a mntfs error */ 433 xp->am_error != 0 || /* There was a mount error */ 434 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ 435 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ 436 ) 437 xp = xp->am_osib; 438 439 return xp; 440 } 441 442 443 /* 444 * Mount an automounter directory. 445 * The automounter is connected into the system 446 * as a user-level NFS server. amfs_mount constructs 447 * the necessary NFS parameters to be given to the 448 * kernel so that it will talk back to us. 449 * 450 * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP). 451 * 452 * NEW: on certain systems, mounting can be done using the 453 * kernel-level automount (autofs) support. In that case, 454 * we don't need NFS at all here. 455 */ 456 int 457 amfs_mount(am_node *mp, mntfs *mf, char *opts) 458 { 459 char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1]; 460 int retry, error = 0, genflags; 461 int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; 462 char *dir = mf->mf_mount; 463 mntent_t mnt; 464 MTYPE_TYPE type; 465 int forced_unmount = 0; /* are we using forced unmounts? */ 466 467 memset((voidp) &mnt, 0, sizeof(mnt)); 468 mnt.mnt_dir = dir; 469 mnt.mnt_fsname = pid_fsname; 470 mnt.mnt_opts = opts; 471 472 #ifdef HAVE_FS_AUTOFS 473 if (mf->mf_flags & MFF_IS_AUTOFS) { 474 type = MOUNT_TYPE_AUTOFS; 475 /* 476 * Make sure that amd's top-level autofs mounts are hidden by default 477 * from df. 478 * XXX: It works ok on Linux, might not work on other systems. 479 */ 480 mnt.mnt_type = "autofs"; 481 } else 482 #endif /* HAVE_FS_AUTOFS */ 483 { 484 type = MOUNT_TYPE_NFS; 485 /* 486 * Make sure that amd's top-level NFS mounts are hidden by default 487 * from df. 488 * If they don't appear to support the either the "ignore" mnttab 489 * option entry, or the "auto" one, set the mount type to "nfs". 490 */ 491 mnt.mnt_type = HIDE_MOUNT_TYPE; 492 } 493 494 retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); 495 if (retry <= 0) 496 retry = 2; /* XXX: default to 2 retries */ 497 498 /* 499 * SET MOUNT ARGS 500 */ 501 502 /* 503 * Make a ``hostname'' string for the kernel 504 */ 505 xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s", 506 get_server_pid(), am_get_hostname(), dir); 507 /* 508 * Most kernels have a name length restriction (64 bytes)... 509 */ 510 if (strlen(fs_hostname) >= MAXHOSTNAMELEN) 511 xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..", 512 sizeof(fs_hostname) - MAXHOSTNAMELEN + 3); 513 #ifdef HOSTNAMESZ 514 /* 515 * ... and some of these restrictions are 32 bytes (HOSTNAMESZ) 516 * If you need to get the definition for HOSTNAMESZ found, you may 517 * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file. 518 */ 519 if (strlen(fs_hostname) >= HOSTNAMESZ) 520 xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..", 521 sizeof(fs_hostname) - HOSTNAMESZ + 3); 522 #endif /* HOSTNAMESZ */ 523 524 /* 525 * Finally we can compute the mount genflags set above, 526 * and add any automounter specific flags. 527 */ 528 genflags = compute_mount_flags(&mnt); 529 #ifdef HAVE_FS_AUTOFS 530 if (on_autofs) 531 genflags |= autofs_compute_mount_flags(&mnt); 532 #endif /* HAVE_FS_AUTOFS */ 533 genflags |= compute_automounter_mount_flags(&mnt); 534 535 again: 536 if (!(mf->mf_flags & MFF_IS_AUTOFS)) { 537 nfs_args_t nfs_args; 538 am_nfs_fh *fhp; 539 am_nfs_handle_t anh; 540 #ifndef HAVE_TRANSPORT_TYPE_TLI 541 u_short port; 542 struct sockaddr_in sin; 543 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 544 545 /* 546 * get fhandle of remote path for automount point 547 */ 548 fhp = get_root_nfs_fh(dir); 549 if (!fhp) { 550 plog(XLOG_FATAL, "Can't find root file handle for %s", dir); 551 return EINVAL; 552 } 553 554 #ifndef HAVE_TRANSPORT_TYPE_TLI 555 /* 556 * Create sockaddr to point to the local machine. 557 */ 558 memset((voidp) &sin, 0, sizeof(sin)); 559 /* as per POSIX, sin_len need not be set (used internally by kernel) */ 560 sin.sin_family = AF_INET; 561 sin.sin_addr = myipaddr; 562 port = hasmntval(&mnt, MNTTAB_OPT_PORT); 563 if (port) { 564 sin.sin_port = htons(port); 565 } else { 566 plog(XLOG_ERROR, "no port number specified for %s", dir); 567 return EINVAL; 568 } 569 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 570 571 /* setup the many fields and flags within nfs_args */ 572 memmove(&anh.v2, fhp, sizeof(*fhp)); 573 #ifdef HAVE_TRANSPORT_TYPE_TLI 574 compute_nfs_args(&nfs_args, 575 &mnt, 576 genflags, 577 nfsncp, 578 NULL, /* remote host IP addr is set below */ 579 NFS_VERSION, /* version 2 */ 580 "udp", 581 &anh, 582 fs_hostname, 583 pid_fsname); 584 /* 585 * IMPORTANT: set the correct IP address AFTERWARDS. It cannot 586 * be done using the normal mechanism of compute_nfs_args(), because 587 * that one will allocate a new address and use NFS_SA_DREF() to copy 588 * parts to it, while assuming that the ip_addr passed is always 589 * a "struct sockaddr_in". That assumption is incorrect on TLI systems, 590 * because they define a special macro HOST_SELF which is DIFFERENT 591 * than localhost (127.0.0.1)! 592 */ 593 nfs_args.addr = &nfsxprt->xp_ltaddr; 594 #else /* not HAVE_TRANSPORT_TYPE_TLI */ 595 compute_nfs_args(&nfs_args, 596 &mnt, 597 genflags, 598 NULL, 599 &sin, 600 NFS_VERSION, /* version 2 */ 601 "udp", 602 &anh, 603 fs_hostname, 604 pid_fsname); 605 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 606 607 /************************************************************************* 608 * NOTE: while compute_nfs_args() works ok for regular NFS mounts * 609 * the toplvl one is not quite regular, and so some options must be * 610 * corrected by hand more carefully, *after* compute_nfs_args() runs. * 611 *************************************************************************/ 612 compute_automounter_nfs_args(&nfs_args, &mnt); 613 614 if (amuDebug(D_TRACE)) { 615 print_nfs_args(&nfs_args, 0); 616 plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags); 617 } 618 619 /* This is it! Here we try to mount amd on its mount points */ 620 error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, 621 retry, type, 0, NULL, mnttab_file_name, on_autofs); 622 623 #ifdef HAVE_TRANSPORT_TYPE_TLI 624 free_knetconfig(nfs_args.knconf); 625 /* 626 * local automounter mounts do not allocate a special address, so 627 * no need to XFREE(nfs_args.addr) under TLI. 628 */ 629 #endif /* HAVE_TRANSPORT_TYPE_TLI */ 630 631 #ifdef HAVE_FS_AUTOFS 632 } else { 633 /* This is it! Here we try to mount amd on its mount points */ 634 error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh, 635 retry, type, 0, NULL, mnttab_file_name, on_autofs); 636 #endif /* HAVE_FS_AUTOFS */ 637 } 638 if (error == 0 || forced_unmount) 639 return error; 640 641 /* 642 * If user wants forced/lazy unmount semantics, then try it iff the 643 * current mount failed with EIO or ESTALE. 644 */ 645 if (gopt.flags & CFM_FORCED_UNMOUNTS) { 646 switch (errno) { 647 case ESTALE: 648 case EIO: 649 forced_unmount = errno; 650 plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path); 651 if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name, 652 AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) { 653 plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path); 654 errno = forced_unmount; 655 } else 656 goto again; 657 default: 658 break; 659 } 660 } 661 662 return error; 663 } 664 665 666 void 667 am_unmounted(am_node *mp) 668 { 669 mntfs *mf = mp->am_mnt; 670 671 if (!foreground) { /* firewall - should never happen */ 672 /* 673 * This is a coding error. Make sure we hear about it! 674 */ 675 plog(XLOG_FATAL, "am_unmounted: illegal use in background (%s)", 676 mp->am_name); 677 notify_child(mp, AMQ_UMNT_OK, 0, 0); /* XXX - be safe? */ 678 return; 679 } 680 681 /* 682 * Do unmounted callback 683 */ 684 if (mf->mf_ops->umounted) 685 mf->mf_ops->umounted(mf); 686 687 /* 688 * This is ugly, but essentially unavoidable. 689 * Sublinks must be treated separately as type==link 690 * when the base type is different. 691 */ 692 if (mp->am_link && mf->mf_ops != &amfs_link_ops) 693 amfs_link_ops.umount_fs(mp, mf); 694 695 #ifdef HAVE_FS_AUTOFS 696 if (mf->mf_flags & MFF_IS_AUTOFS) 697 autofs_release_fh(mp); 698 if (mp->am_flags & AMF_AUTOFS) 699 autofs_umount_succeeded(mp); 700 #endif /* HAVE_FS_AUTOFS */ 701 702 /* 703 * Clean up any directories that were made 704 * 705 * If we remove the mount point of a pending mount, any queued access 706 * to it will fail. So don't do it in that case. 707 * Also don't do it if the refcount is > 1. 708 */ 709 if (mf->mf_flags & MFF_MKMNT && 710 mf->mf_refc == 1 && 711 !(mp->am_flags & AMF_REMOUNT)) { 712 plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount); 713 rmdirs(mf->mf_mount); 714 mf->mf_flags &= ~MFF_MKMNT; 715 } 716 717 /* 718 * If this is a pseudo-directory then adjust the link count 719 * in the parent 720 */ 721 if (mp->am_parent && mp->am_fattr.na_type == NFDIR) 722 --mp->am_parent->am_fattr.na_nlink; 723 724 /* 725 * Update mtime of parent node 726 */ 727 if (mp->am_parent && mp->am_parent->am_mnt) 728 clocktime(&mp->am_parent->am_fattr.na_mtime); 729 730 if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) { 731 char *fname = strdup(mp->am_name); 732 am_node *mp_parent = mp->am_parent; 733 mntfs *mf_parent = mp_parent->am_mnt; 734 am_node fake_mp; 735 int error = 0; 736 737 /* 738 * We need to use notify_child() after free_map(), so save enough 739 * to do that in fake_mp. 740 */ 741 fake_mp.am_fd[1] = mp->am_fd[1]; 742 mp->am_fd[1] = -1; 743 744 free_map(mp); 745 plog(XLOG_INFO, "am_unmounted: remounting %s", fname); 746 mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE); 747 if (mp && error < 0) 748 mp = mf_parent->mf_ops->mount_child(mp, &error); 749 if (error > 0) { 750 errno = error; 751 plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname); 752 notify_child(&fake_mp, AMQ_UMNT_OK, 0, 0); 753 } else { 754 notify_child(&fake_mp, AMQ_UMNT_FAILED, EBUSY, 0); 755 } 756 XFREE(fname); 757 } else { 758 /* 759 * We have a race here. 760 * If this node has a pending mount and amd is going down (unmounting 761 * everything in the process), then we could potentially free it here 762 * while a struct continuation still has a reference to it. So when 763 * amfs_cont is called, it blows up. 764 * We avoid the race by refusing to free any nodes that have 765 * pending mounts (defined as having a non-NULL am_mfarray). 766 */ 767 notify_child(mp, AMQ_UMNT_OK, 0, 0); /* do this regardless */ 768 if (!mp->am_mfarray) 769 free_map(mp); 770 } 771 } 772 773 774 /* 775 * Fork the automounter 776 * 777 * TODO: Need a better strategy for handling errors 778 */ 779 static int 780 dofork(void) 781 { 782 int pid; 783 784 top: 785 pid = fork(); 786 787 if (pid < 0) { /* fork error, retry in 1 second */ 788 sleep(1); 789 goto top; 790 } 791 if (pid == 0) { /* child process (foreground==false) */ 792 am_set_mypid(); 793 foreground = 0; 794 } else { /* parent process, has one more child */ 795 NumChildren++; 796 } 797 798 return pid; 799 } 800 801 802 int 803 background(void) 804 { 805 int pid = dofork(); 806 807 if (pid == 0) { 808 dlog("backgrounded"); 809 foreground = 0; 810 } else 811 dlog("forked process %d", pid); 812 return pid; 813 } 814