From: Erez Zadok Date: Wed, 20 Jul 2005 03:32:30 +0000 (+0000) Subject: * conf/nfs_prot/nfs_prot_aix5_2.h: define compatible X-Git-Tag: am-utils-6_1_1~17 X-Git-Url: https://git.fsl.cs.sunysb.edu/?a=commitdiff_plain;h=dc22f4bde8a5817fb438b89b1d0c2eecbe710dcb;p=am-utils-6.1.git * conf/nfs_prot/nfs_prot_aix5_2.h: define compatible forced-unmount flag. * conf/umount/umount_default.c (umount_fs): if regular umount got EBUSY, EIO, or ESTALE, then try forced unmount, if supported. Try umount2 (Solaris) or uvmount (AIX). * conf/umount/umount_{bsd44,osf}.c (umount_fs): if default umount() failed with EIO or ESTALE, also try forced unmount. * amd/autil.c (amfs_mount), amd/amfs_toplvl.c (amfs_toplvl_umount): enable forced/lazy unmounts only if user asked for it (and dlog it). * scripts/amd.conf.5: document new force_unmount global parameter. * doc/am-utils.texi (forced_unmounts Parameter): document new global parameter. * amd/conf.c (gopt_forced_unmounts): process forced_unmounts option (default "no"). Exit with an error if user specified the option as "yes" but configure couldn't find support for either the MNT_FORCE or MNT_DETACH flags. * scripts/amd.conf-sample (forced_unmounts): example usage of new option. * amd/amd.h (CFM_FORCED_UNMOUNTS): new flag for forced_unmounts option. * m4/macros/header_templates.m4: templates for _DETACH and _FORCE. * configure.in: check for generic u/mount options "detach" and "force". * include/am_utils.h (UMOUNT_FS, umount_fs): new prototypes. Define AMU_UMOUNT flags for force, detach, and autofs. * amd/amfs_toplvl.c (amfs_toplvl_umount): pass _FORCE and _DETACH unmount flags unconditionally. Pass them to UMOUNT_FS(). * conf/nfs_prot/nfs_prot_linux.h: define MNT_FORCE and MNT_DETACH if needed, because some Linux systems don't define them (presumably because it would be too dangerous to expose these flags to users). * amd/autil.c (amfs_mount): if mount(2) failed with ESTALE or EIO, then assume that we're in trouble, possibly because a previous mount is hung. So, first try to force a lazy unmount of the old mount. If the forced unmount worked, then try again to mount the desired file system. If the forced unmount failed, then don't retry: just return an error. * amd/amfs_generic.c (amfs_generic_umount), amd/amfs_host.c (amfs_host_umount), amd/ops_cachefs.c (cachefs_umount), amd/ops_cdfs.c (cdfs_umount), amd/ops_efs.c (efs_umount), amd/ops_lofs.c (lofs_umount), amd/ops_nfs.c (nfs_umount), amd/ops_pcfs.c (pcfs_umount), amd/ops_ufs.c (ufs_umount), amd/ops_xfs.c (xfs_umount), conf/umount/umount_bsd44.c (umount_fs), conf/umount/umount_default.c (umount_fs), conf/umount/umount_linux.c (umount_fs), conf/umount/umount_osf.c (umount_fs): compute and use unmount specific flags. --- diff --git a/ChangeLog b/ChangeLog index df30c4f..8d5c953 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,70 @@ +2005-07-19 Erez Zadok + + * conf/nfs_prot/nfs_prot_aix5_2.h: define compatible + forced-unmount flag. + + * conf/umount/umount_default.c (umount_fs): if regular umount got + EBUSY, EIO, or ESTALE, then try forced unmount, if supported. Try + umount2 (Solaris) or uvmount (AIX). + + * conf/umount/umount_{bsd44,osf}.c (umount_fs): if default + umount() failed with EIO or ESTALE, also try forced unmount. + +2005-07-18 Erez Zadok + + * amd/autil.c (amfs_mount), amd/amfs_toplvl.c + (amfs_toplvl_umount): enable forced/lazy unmounts only if user + asked for it (and dlog it). + + * scripts/amd.conf.5: document new force_unmount global parameter. + + * doc/am-utils.texi (forced_unmounts Parameter): document new + global parameter. + + * amd/conf.c (gopt_forced_unmounts): process forced_unmounts + option (default "no"). Exit with an error if user specified the + option as "yes" but configure couldn't find support for either the + MNT_FORCE or MNT_DETACH flags. + + * scripts/amd.conf-sample (forced_unmounts): example usage of new + option. + + * amd/amd.h (CFM_FORCED_UNMOUNTS): new flag for forced_unmounts + option. + + * m4/macros/header_templates.m4: templates for _DETACH and _FORCE. + + * configure.in: check for generic u/mount options "detach" and + "force". + + * include/am_utils.h (UMOUNT_FS, umount_fs): new prototypes. + Define AMU_UMOUNT flags for force, detach, and autofs. + + * amd/amfs_toplvl.c (amfs_toplvl_umount): pass _FORCE and _DETACH + unmount flags unconditionally. Pass them to UMOUNT_FS(). + + * conf/nfs_prot/nfs_prot_linux.h: define MNT_FORCE and MNT_DETACH + if needed, because some Linux systems don't define them + (presumably because it would be too dangerous to expose these + flags to users). + + * amd/autil.c (amfs_mount): if mount(2) failed with ESTALE or EIO, + then assume that we're in trouble, possibly because a previous + mount is hung. So, first try to force a lazy unmount of the old + mount. If the forced unmount worked, then try again to mount the + desired file system. If the forced unmount failed, then don't + retry: just return an error. + + * amd/amfs_generic.c (amfs_generic_umount), amd/amfs_host.c + (amfs_host_umount), amd/ops_cachefs.c (cachefs_umount), + amd/ops_cdfs.c (cdfs_umount), amd/ops_efs.c (efs_umount), + amd/ops_lofs.c (lofs_umount), amd/ops_nfs.c (nfs_umount), + amd/ops_pcfs.c (pcfs_umount), amd/ops_ufs.c (ufs_umount), + amd/ops_xfs.c (xfs_umount), conf/umount/umount_bsd44.c + (umount_fs), conf/umount/umount_default.c (umount_fs), + conf/umount/umount_linux.c (umount_fs), conf/umount/umount_osf.c + (umount_fs): compute and use unmount specific flags. + 2005-07-16 Erez Zadok * configure.in: better help for users, if nfs_args can't be found. diff --git a/NEWS b/NEWS index 6531807..80a54f0 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,15 @@ *** Notes specific to am-utils version 6.1.1-rc1 +New amd.conf global parameter: forced_unmounts (default to "no"). If set to +"yes," and the client OS supports forced or lazy unmounts, then Amd will +attempt to use them if it gets any of three serious error conditions when +trying to unmount an existing mount point or mount on top of one: EIO, +ESTALE, or EBUSY. This could be useful to recover from serious conditions +such as hardware failure of mounted disks, or NFS servers which are down +permanently, were migrated, or changed their IP address. Only +"type:=toplvl" mounts hung with EBUSY are forcibly unmounted using this +option, which is useful to recover from a hung Amd). + - minor new ports: i386-pc-linux-fc4 i386-pc-linux-suse9.3 diff --git a/amd/amd.h b/amd/amd.h index 30a5706..e5b1ab6 100644 --- a/amd/amd.h +++ b/amd/amd.h @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: amd.h,v 1.66 2005/06/04 16:34:33 ezk Exp $ + * $Id: amd.h,v 1.67 2005/07/20 03:32:30 ezk Exp $ * */ @@ -58,22 +58,23 @@ #endif /* MOUNT_TABLE_ON_FILE */ /* options for amd.conf */ -#define CFM_BROWSABLE_DIRS 0x0001 -#define CFM_MOUNT_TYPE_AUTOFS 0x0002 /* use kernel autofs support */ -#define CFM_SELECTORS_IN_DEFAULTS 0x0004 -#define CFM_NORMALIZE_HOSTNAMES 0x0008 -#define CFM_PROCESS_LOCK 0x0010 -#define CFM_PRINT_PID 0x0020 -#define CFM_RESTART_EXISTING_MOUNTS 0x0040 -#define CFM_SHOW_STATFS_ENTRIES 0x0080 -#define CFM_FULLY_QUALIFIED_HOSTS 0x0100 -#define CFM_BROWSABLE_DIRS_FULL 0x0200 /* allow '/' in readdir() */ -#define CFM_UNMOUNT_ON_EXIT 0x0400 /* when amd finishing */ -#define CFM_USE_TCPWRAPPERS 0x0800 -#define CFM_AUTOFS_USE_LOFS 0x1000 -#define CFM_NFS_INSECURE_PORT 0x2000 -#define CFM_DOMAIN_STRIP 0x4000 -#define CFM_NORMALIZE_SLASHES 0x8000 /* normalize slashes? */ +#define CFM_BROWSABLE_DIRS 0x00000001 +#define CFM_MOUNT_TYPE_AUTOFS 0x00000002 /* use kernel autofs support */ +#define CFM_SELECTORS_IN_DEFAULTS 0x00000004 +#define CFM_NORMALIZE_HOSTNAMES 0x00000008 +#define CFM_PROCESS_LOCK 0x00000010 +#define CFM_PRINT_PID 0x00000020 +#define CFM_RESTART_EXISTING_MOUNTS 0x00000040 +#define CFM_SHOW_STATFS_ENTRIES 0x00000080 +#define CFM_FULLY_QUALIFIED_HOSTS 0x00000100 +#define CFM_BROWSABLE_DIRS_FULL 0x00000200 /* allow '/' in readdir() */ +#define CFM_UNMOUNT_ON_EXIT 0x00000400 /* when amd finishing */ +#define CFM_USE_TCPWRAPPERS 0x00000800 +#define CFM_AUTOFS_USE_LOFS 0x00001000 +#define CFM_NFS_INSECURE_PORT 0x00002000 +#define CFM_DOMAIN_STRIP 0x00004000 +#define CFM_NORMALIZE_SLASHES 0x00008000 /* normalize slashes? */ +#define CFM_FORCED_UNMOUNTS 0x00010000 /* normalize slashes? */ /* defaults global flags: plock, tcpwrappers, and autofs/lofs */ #define CFM_DEFAULT_FLAGS (CFM_PROCESS_LOCK|CFM_USE_TCPWRAPPERS|CFM_AUTOFS_USE_LOFS|CFM_DOMAIN_STRIP|CFM_NORMALIZE_SLASHES) diff --git a/amd/amfs_generic.c b/amd/amfs_generic.c index 3d37194..5b84e75 100644 --- a/amd/amfs_generic.c +++ b/amd/amfs_generic.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: amfs_generic.c,v 1.30 2005/05/16 18:08:53 ezk Exp $ + * $Id: amfs_generic.c,v 1.31 2005/07/20 03:32:30 ezk Exp $ * */ @@ -1220,9 +1220,9 @@ amfs_generic_umount(am_node *mp, mntfs *mf) int error = 0; #ifdef HAVE_FS_AUTOFS - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; if (mf->mf_flags & MFF_IS_AUTOFS) - error = UMOUNT_FS(mp->am_path, mnttab_file_name, on_autofs); + error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); #endif /* HAVE_FS_AUTOFS */ return error; diff --git a/amd/amfs_host.c b/amd/amfs_host.c index 781b279..de5f976 100644 --- a/amd/amfs_host.c +++ b/amd/amfs_host.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: amfs_host.c,v 1.31 2005/05/02 00:27:47 ottavio Exp $ + * $Id: amfs_host.c,v 1.32 2005/07/20 03:32:30 ezk Exp $ * */ @@ -529,7 +529,7 @@ static int amfs_host_umount(am_node *am, mntfs *mf) { mntlist *ml, *mprev; - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; int xerror = 0; /* @@ -568,7 +568,7 @@ amfs_host_umount(am_node *am, mntfs *mf) /* * Unmount "dir" */ - error = UMOUNT_FS(dir, mnttab_file_name, on_autofs); + error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags); /* * Keep track of errors */ @@ -579,9 +579,9 @@ amfs_host_umount(am_node *am, mntfs *mf) * 'xerror' is the return value for this function. * * We do not want to pass ENOENT as an error because if the - * directory does not exists our work is done anyway. + * directory does not exists our work is done anyway. */ - if (!xerror && error != ENOENT) + if (!xerror && error != ENOENT) xerror = error; if (error != EBUSY) { errno = error; diff --git a/amd/amfs_toplvl.c b/amd/amfs_toplvl.c index 56a5b19..be7daf7 100644 --- a/amd/amfs_toplvl.c +++ b/amd/amfs_toplvl.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: amfs_toplvl.c,v 1.38 2005/04/17 03:05:54 ezk Exp $ + * $Id: amfs_toplvl.c,v 1.39 2005/07/20 03:32:30 ezk Exp $ * */ @@ -219,9 +219,15 @@ int amfs_toplvl_umount(am_node *mp, mntfs *mf) { struct stat stb; - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; int error; + /* if user wants forced/lazy unmount semantics, set those flags */ + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + dlog("enabling forced/lazy unmounts for toplvl node %s", mp->am_path); + unmount_flags |= (AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH); + } + again: /* * The lstat is needed if this mount is type=direct. @@ -245,7 +251,7 @@ again: goto out; } - error = UMOUNT_FS(mp->am_path, mnttab_file_name, on_autofs); + error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); if (error == EBUSY) { #ifdef HAVE_FS_AUTOFS /* diff --git a/amd/autil.c b/amd/autil.c index e57c6da..7f43af8 100644 --- a/amd/autil.c +++ b/amd/autil.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: autil.c,v 1.51 2005/04/17 03:05:54 ezk Exp $ + * $Id: autil.c,v 1.52 2005/07/20 03:32:30 ezk Exp $ * */ @@ -516,6 +516,7 @@ amfs_mount(am_node *mp, mntfs *mf, char *opts) char *dir = mf->mf_mount; mntent_t mnt; MTYPE_TYPE type; + int forced_unmount = 0; /* are we using forced unmounts? */ memset((voidp) &mnt, 0, sizeof(mnt)); mnt.mnt_dir = dir; @@ -583,6 +584,7 @@ amfs_mount(am_node *mp, mntfs *mf, char *opts) #endif /* HAVE_FS_AUTOFS */ genflags |= compute_automounter_mount_flags(&mnt); +again: if (!(mf->mf_flags & MFF_IS_AUTOFS)) { nfs_args_t nfs_args; am_nfs_fh *fhp; @@ -684,6 +686,29 @@ amfs_mount(am_node *mp, mntfs *mf, char *opts) retry, type, 0, NULL, mnttab_file_name, on_autofs); #endif /* HAVE_FS_AUTOFS */ } + if (error == 0 || forced_unmount) + return error; + + /* + * If user wants forced/lazy unmount semantics, then try it iff the + * current mount failed with EIO or ESTALE. + */ + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + switch (errno) { + case ESTALE: + case EIO: + forced_unmount = errno; + plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path); + if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name, + AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) { + plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path); + errno = forced_unmount; + } else + goto again; + default: + break; + } + } return error; } diff --git a/amd/conf.c b/amd/conf.c index 4a9faf0..73877c5 100644 --- a/amd/conf.c +++ b/amd/conf.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: conf.c,v 1.32 2005/04/17 03:05:54 ezk Exp $ + * $Id: conf.c,v 1.33 2005/07/20 03:32:30 ezk Exp $ * */ @@ -86,6 +86,7 @@ static int gopt_debug_options(const char *val); static int gopt_dismount_interval(const char *val); static int gopt_domain_strip(const char *val); static int gopt_exec_map_timeout(const char *val); +static int gopt_forced_unmounts(const char *val); static int gopt_full_os(const char *val); static int gopt_fully_qualified_hosts(const char *val); static int gopt_hesiod_base(const char *val); @@ -163,6 +164,7 @@ static struct _func_map glob_functable[] = { {"dismount_interval", gopt_dismount_interval}, {"domain_strip", gopt_domain_strip}, {"exec_map_timeout", gopt_exec_map_timeout}, + {"forced_unmounts", gopt_forced_unmounts}, {"fully_qualified_hosts", gopt_fully_qualified_hosts}, {"full_os", gopt_full_os}, {"hesiod_base", gopt_hesiod_base}, @@ -492,6 +494,27 @@ gopt_exec_map_timeout(const char *val) } +static int +gopt_forced_unmounts(const char *val) +{ + if (STREQ(val, "yes")) { +#if !defined(MNT2_GEN_OPT_DETACH) && !defined(MNT2_GEN_OPT_FORCE) + fprintf(stderr, "conf: forced_unmounts unsupported on this system.\n"); + return 1; +#else /* defined(MNT2_GEN_OPT_DETACH) || defined(MNT2_GEN_OPT_FORCE) */ + gopt.flags |= CFM_FORCED_UNMOUNTS; + return 0; +#endif /* defined(MNT2_GEN_OPT_DETACH) || defined(MNT2_GEN_OPT_FORCE) */ + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_FORCED_UNMOUNTS; + return 0; + } + + fprintf(stderr, "conf: unknown value to unmount_on_exit \"%s\"\n", val); + return 1; /* unknown value */ +} + + static int gopt_full_os(const char *val) { diff --git a/amd/ops_cachefs.c b/amd/ops_cachefs.c index 9574949..54e9f13 100644 --- a/amd/ops_cachefs.c +++ b/amd/ops_cachefs.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: ops_cachefs.c,v 1.18 2005/03/06 03:19:01 ezk Exp $ + * $Id: ops_cachefs.c,v 1.19 2005/07/20 03:32:30 ezk Exp $ * */ @@ -229,10 +229,10 @@ cachefs_mount(am_node *am, mntfs *mf) static int cachefs_umount(am_node *am, mntfs *mf) { - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; int error; - error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); /* * In the case of cachefs, we must fsck the cache directory. Otherwise, diff --git a/amd/ops_cdfs.c b/amd/ops_cdfs.c index 7275558..2a4b04b 100644 --- a/amd/ops_cdfs.c +++ b/amd/ops_cdfs.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: ops_cdfs.c,v 1.24 2005/01/03 20:56:45 ezk Exp $ + * $Id: ops_cdfs.c,v 1.25 2005/07/20 03:32:30 ezk Exp $ * */ @@ -222,6 +222,7 @@ cdfs_mount(am_node *am, mntfs *mf) static int cdfs_umount(am_node *am, mntfs *mf) { - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; - return UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/amd/ops_efs.c b/amd/ops_efs.c index e19f9ad..9ae8dad 100644 --- a/amd/ops_efs.c +++ b/amd/ops_efs.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: ops_efs.c,v 1.19 2005/01/03 20:56:45 ezk Exp $ + * $Id: ops_efs.c,v 1.20 2005/07/20 03:32:30 ezk Exp $ * */ @@ -165,7 +165,8 @@ efs_mount(am_node *am, mntfs *mf) static int efs_umount(am_node *am, mntfs *mf) { - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; - return UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/amd/ops_lofs.c b/amd/ops_lofs.c index 2ea06b1..c0bc7fa 100644 --- a/amd/ops_lofs.c +++ b/amd/ops_lofs.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: ops_lofs.c,v 1.18 2005/01/03 20:56:45 ezk Exp $ + * $Id: ops_lofs.c,v 1.19 2005/07/20 03:32:30 ezk Exp $ * */ @@ -153,6 +153,7 @@ lofs_mount(am_node *am, mntfs *mf) static int lofs_umount(am_node *am, mntfs *mf) { - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; - return UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/amd/ops_nfs.c b/amd/ops_nfs.c index 2abfb17..a22972a 100644 --- a/amd/ops_nfs.c +++ b/amd/ops_nfs.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: ops_nfs.c,v 1.43 2005/06/04 16:34:33 ezk Exp $ + * $Id: ops_nfs.c,v 1.44 2005/07/20 03:32:30 ezk Exp $ * */ @@ -925,8 +925,8 @@ nfs_mount(am_node *am, mntfs *mf) static int nfs_umount(am_node *am, mntfs *mf) { - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; - int error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + int error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); /* * Here is some code to unmount 'restarted' file systems. @@ -955,12 +955,14 @@ nfs_umount(am_node *am, mntfs *mf) if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) && new_mf->mf_mount[len] == '/') { - UMOUNT_FS(new_mf->mf_mount, mnttab_file_name, 0); + int new_unmount_flags = + (new_mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + UMOUNT_FS(new_mf->mf_mount, mnttab_file_name, new_unmount_flags); didsome = 1; } } if (didsome) - error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } if (error) return error; diff --git a/amd/ops_pcfs.c b/amd/ops_pcfs.c index 0604b44..89e7dca 100644 --- a/amd/ops_pcfs.c +++ b/amd/ops_pcfs.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: ops_pcfs.c,v 1.19 2005/01/03 20:56:45 ezk Exp $ + * $Id: ops_pcfs.c,v 1.20 2005/07/20 03:32:30 ezk Exp $ * */ @@ -184,6 +184,7 @@ pcfs_mount(am_node *am, mntfs *mf) static int pcfs_umount(am_node *am, mntfs *mf) { - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; - return UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/amd/ops_ufs.c b/amd/ops_ufs.c index 2f5a760..c8f5de7 100644 --- a/amd/ops_ufs.c +++ b/amd/ops_ufs.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: ops_ufs.c,v 1.18 2005/01/03 20:56:45 ezk Exp $ + * $Id: ops_ufs.c,v 1.19 2005/07/20 03:32:30 ezk Exp $ * */ @@ -174,6 +174,7 @@ ufs_mount(am_node *am, mntfs *mf) static int ufs_umount(am_node *am, mntfs *mf) { - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; - return UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/amd/ops_xfs.c b/amd/ops_xfs.c index 646e089..26b6148 100644 --- a/amd/ops_xfs.c +++ b/amd/ops_xfs.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: ops_xfs.c,v 1.20 2005/01/03 20:56:45 ezk Exp $ + * $Id: ops_xfs.c,v 1.21 2005/07/20 03:32:30 ezk Exp $ * */ @@ -165,6 +165,7 @@ xfs_mount(am_node *am, mntfs *mf) static int xfs_umount(am_node *am, mntfs *mf) { - int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; - return UMOUNT_FS(mf->mf_mount, mnttab_file_name, on_autofs); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/conf/nfs_prot/nfs_prot_aix5_2.h b/conf/nfs_prot/nfs_prot_aix5_2.h index 2a7c9b7..0202a90 100644 --- a/conf/nfs_prot/nfs_prot_aix5_2.h +++ b/conf/nfs_prot/nfs_prot_aix5_2.h @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: nfs_prot_aix5_2.h,v 1.10 2005/05/26 13:36:35 ezk Exp $ + * $Id: nfs_prot_aix5_2.h,v 1.11 2005/07/20 03:32:30 ezk Exp $ * */ @@ -86,6 +86,11 @@ struct thread_credentials; # define MNTNAMLEN 255 #endif /* not MNTNAMLEN */ +/* compatible macro name with other OSs */ +#ifdef UVMNT_FORCE +# define MS_FORCE UVMNT_FORCE +#endif /* UVMNT_FORCE */ + /********************************************************************************/ /* * NFS mount option flags (out of mount.h) diff --git a/conf/nfs_prot/nfs_prot_linux.h b/conf/nfs_prot/nfs_prot_linux.h index 610ddfc..8dbad6f 100644 --- a/conf/nfs_prot/nfs_prot_linux.h +++ b/conf/nfs_prot/nfs_prot_linux.h @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: nfs_prot_linux.h,v 1.21 2005/01/03 20:56:45 ezk Exp $ + * $Id: nfs_prot_linux.h,v 1.22 2005/07/20 03:32:30 ezk Exp $ * */ @@ -83,9 +83,21 @@ #ifndef MNTTYPE_NFS3 # define MNTTYPE_NFS3 "nfs" #endif /* not MNTTYPE_NFS3 */ - #endif /* HAVE_FS_NFS3 */ +/* + * These two force/lazy unmount flags are sometimes missing from some linux + * systems' headers. + */ +#ifdef HAVE_UMOUNT2 +# ifndef MNT_FORCE +# define MNT_FORCE 0x1 /* from */ +# endif /* not MNT_FORCE */ +# ifndef MNT_DETACH +# define MNT_DETACH 0x2 /* from kernel */ +# endif /* not MNT_DETACH */ +#endif /* HAVE_UMOUNT2 */ + /* XXX: hack until we have a better way to detect /dev/loop devices */ #ifdef HAVE_LINUX_LOOP_H # define HAVE_LOOP_DEVICE diff --git a/conf/umount/umount_bsd44.c b/conf/umount/umount_bsd44.c index 36096e5..05ca413 100644 --- a/conf/umount/umount_bsd44.c +++ b/conf/umount/umount_bsd44.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: umount_bsd44.c,v 1.12 2005/01/03 20:56:45 ezk Exp $ + * $Id: umount_bsd44.c,v 1.13 2005/07/20 03:32:30 ezk Exp $ * */ @@ -53,7 +53,7 @@ int -umount_fs(char *mntdir, const char *mnttabname, int on_autofs) +umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) { int error; @@ -75,6 +75,20 @@ eintr: dlog("%s: unmount: %m", mntdir); goto eintr; +#ifdef MNT2_GEN_OPT_FORCE + case EBUSY: + case EIO: + case ESTALE: + /* caller determines if forced unmounts should be used */ + if (unmount_flags & AMU_UMOUNT_FORCE) { + if ((error = unmount(mntdir, MNT2_GEN_OPT_FORCE)) < 0) + error = errno; + else + return error; + } + /* fallthrough */ +#endif /* MNT2_GEN_OPT_FORCE */ + default: dlog("%s: unmount: %m", mntdir); break; diff --git a/conf/umount/umount_default.c b/conf/umount/umount_default.c index 3bd0572..817d0dc 100644 --- a/conf/umount/umount_default.c +++ b/conf/umount/umount_default.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: umount_default.c,v 1.14 2005/01/17 19:31:54 ib42 Exp $ + * $Id: umount_default.c,v 1.15 2005/07/20 03:32:30 ezk Exp $ * */ @@ -53,7 +53,7 @@ int -umount_fs(char *mntdir, const char *mnttabname, int on_autofs) +umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) { mntlist *mlist, *mp, *mp_save = 0; int error = 0; @@ -83,7 +83,7 @@ umount_fs(char *mntdir, const char *mnttabname, int on_autofs) #endif /* MOUNT_TABLE_ON_FILE */ #ifdef NEED_AUTOFS_SPACE_HACK - if (on_autofs) { + if (unmount_flags & AMU_UMOUNT_AUTOFS) { char *mnt_dir_save = mp_save->mnt->mnt_dir; mp_save->mnt->mnt_dir = autofs_strdup_space_hack(mnt_dir_save); error = UNMOUNT_TRAP(mp_save->mnt); @@ -93,7 +93,7 @@ umount_fs(char *mntdir, const char *mnttabname, int on_autofs) #endif /* NEED_AUTOFS_SPACE_HACK */ error = UNMOUNT_TRAP(mp_save->mnt); if (error < 0) { - switch (error = errno) { + switch ((error = errno)) { case EINVAL: case ENOTBLK: plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); @@ -110,6 +110,26 @@ umount_fs(char *mntdir, const char *mnttabname, int on_autofs) plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); break; +#if defined(MNT2_GEN_OPT_FORCE) && (defined(HAVE_UMOUNT2) || defined(HAVE_UVMOUNT)) + case EBUSY: + case EIO: + case ESTALE: + /* caller determines if forced unmounts should be used */ + if (unmount_flags & AMU_UMOUNT_FORCE) { +# ifdef HAVE_UMOUNT2 + error = umount2(mntdir, MNT2_GEN_OPT_FORCE); /* Solaris */ +# else /* not HAVE_UMOUNT2 */ +# ifdef HAVE_UVMOUNT + error = uvmount(mp_save->mnt->mnt_passno, MNT2_GEN_OPT_FORCE); /* AIX */ +# endif /* HAVE_UVMOUNT */ +# endif /* not HAVE_UMOUNT2 */ + if (error < 0) + error = errno; + else + break; /* all is OK */ + } + /* fallthrough */ +#endif /* MNT2_GEN_OPT_FORCE && (HAVE_UMOUNT2 || HAVE_UVMOUNT) */ default: dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); break; diff --git a/conf/umount/umount_linux.c b/conf/umount/umount_linux.c index 4e5e4cf..f4b6e04 100644 --- a/conf/umount/umount_linux.c +++ b/conf/umount/umount_linux.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: umount_linux.c,v 1.5 2005/07/10 21:41:49 ezk Exp $ + * $Id: umount_linux.c,v 1.6 2005/07/20 03:32:30 ezk Exp $ * */ @@ -54,8 +54,9 @@ int -umount_fs(char *mntdir, const char *mnttabname, int on_autofs) +umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) { + struct stat dummy; mntlist *mlist, *mp, *mp_save = 0; int error = 0; @@ -83,7 +84,27 @@ umount_fs(char *mntdir, const char *mnttabname, int on_autofs) unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ - error = UNMOUNT_TRAP(mp_save->mnt); +#if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) + /* + * If user asked to try forced unmounts, then do a quick check to see if + * the mount point is hung badly. If so, then try to detach it by + * force; if the latter works, we're done. + * + * XXX: the stat() below may hang this unmount attempt of a toplvl + * mount. In that case, you may have to kill -9 the Amd process. A + * better way to handle this would be to check mtab for an old amd + * process, send a kill -0 to it to see if the Amd process is alive, and + * only do the forced unmount if the older Amd process died. + */ + if ((unmount_flags & AMU_UMOUNT_DETACH) && + (stat(mp_save->mnt->mnt_dir, &dummy) < 0 && + (errno == ESTALE || errno == EIO))) { + plog(XLOG_INFO, "Forcibly unmounting stale/bad %s (%m)", + mp_save->mnt->mnt_dir); + error = umount2(mp_save->mnt->mnt_dir, MNT2_GEN_OPT_DETACH); + } else +#endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) */ + error = UNMOUNT_TRAP(mp_save->mnt); if (error < 0) { plog(XLOG_WARNING, "unmount(%s) failed: %m", mp_save->mnt->mnt_dir); switch ((error = errno)) { @@ -103,20 +124,24 @@ umount_fs(char *mntdir, const char *mnttabname, int on_autofs) plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); break; -#ifdef HAVE_UMOUNT2 -# ifndef MNT_DETACH -# define MNT_DETACH 0x2 /* from kernel */ -# endif /* not MNT_DETACH */ - case EIO: - case ESTALE: - plog(XLOG_ERROR, "umount %s failed (retrying): %m", mp_save->mnt->mnt_dir); - error = umount2(mp_save->mnt->mnt_dir, MNT_DETACH | MNT_FORCE); - if (error < 0) { - dlog("%s: unmount2(detach+force): %m", mp_save->mnt->mnt_dir); +#if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) + case EBUSY: + /* + * Caller determines if forced unmounts should be used now (for + * EBUSY). If caller asked to force an unmount, *and* the above + * "trivial" unmount attempt failed with EBUSY, then try to force + * the unmount. + */ + if (unmount_flags & AMU_UMOUNT_FORCE) { + error = umount2(mp_save->mnt->mnt_dir, MNT2_GEN_OPT_FORCE); + if (error < 0) { + plog(XLOG_WARNING, "%s: unmount/detach: %m", + mp_save->mnt->mnt_dir); error = errno; + } } break; -#endif /* HAVE_UMOUNT2 */ +#endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) */ default: dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); diff --git a/conf/umount/umount_osf.c b/conf/umount/umount_osf.c index dbb6f4a..57719a9 100644 --- a/conf/umount/umount_osf.c +++ b/conf/umount/umount_osf.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: umount_osf.c,v 1.11 2005/01/03 20:56:45 ezk Exp $ + * $Id: umount_osf.c,v 1.12 2005/07/20 03:32:30 ezk Exp $ * */ @@ -53,7 +53,7 @@ int -umount_fs(char *fs_name, const char *mnttabname, int on_autofs) +umount_fs(char *fs_name, const char *mnttabname, int unmount_flags) { int error; @@ -78,6 +78,20 @@ eintr: dlog("%s: unmount: %m", fs_name); goto eintr; +#ifdef MNT2_GEN_OPT_FORCE + case EBUSY: + case EIO: + case ESTALE: + /* caller determines if forced unmounts should be used */ + if (unmount_flags & AMU_UMOUNT_FORCE) { + if ((error = umount(mntdir, MNT2_GEN_OPT_FORCE)) < 0) + error = errno; + else + return error; + } + /* fallthrough */ +#endif /* MNT2_GEN_OPT_FORCE */ + default: dlog("%s: unmount: %m", fs_name); break; diff --git a/configure.in b/configure.in index e8e4edb..e8fcb8c 100644 --- a/configure.in +++ b/configure.in @@ -55,7 +55,7 @@ AH_BOTTOM([ dnl dnl AC_CONFIG_AUX_DIR(m4) AC_PREREQ(2.52) -AC_REVISION($Revision: 1.103 $) +AC_REVISION($Revision: 1.104 $) AC_COPYRIGHT([Copyright (c) 1997-2005 Erez Zadok]) dnl find out system type AC_MSG_NOTICE(*** SYSTEM TYPES ***) @@ -1063,6 +1063,8 @@ AMU_CHECK_MNT2_GEN_OPTS( \ bind \ cache \ data \ + detach \ + force \ fss \ grpid \ ignore \ diff --git a/doc/am-utils.texi b/doc/am-utils.texi index f1e64cc..db3f9aa 100644 --- a/doc/am-utils.texi +++ b/doc/am-utils.texi @@ -38,7 +38,7 @@ @c @c %W% (Berkeley) %G% @c -@c $Id: am-utils.texi,v 1.102 2005/05/03 03:14:31 ezk Exp $ +@c $Id: am-utils.texi,v 1.103 2005/07/20 03:32:30 ezk Exp $ @c @setfilename am-utils.info @@ -4277,6 +4277,7 @@ The following parameters are applicable to the @samp{[global]} section only. * dismount_interval Parameter:: * domain_strip Parameter:: * exec_map_timeout Parameter:: +* forced_unmounts Parameter:: * full_os Parameter:: * fully_qualified_hosts Parameter:: * hesiod_base Parameter:: @@ -4423,7 +4424,7 @@ the same maps (as you may have hosts whose domain-stripped name is identical). @c ---------------------------------------------------------------- -@node exec_map_timeout Parameter, full_os Parameter, domain_strip Parameter, Global Parameters +@node exec_map_timeout Parameter, forced_unmounts Parameter, domain_strip Parameter, Global Parameters @comment node-name, next, previous, up @subsection @t{exec_map_timeout} Parameter @cindex exec_map_timeout Parameter @@ -4437,7 +4438,40 @@ is queried, @i{Amd} is essentially waiting and is thus not responding to any other queries. @xref{Executable maps}. @c ---------------------------------------------------------------- -@node full_os Parameter, fully_qualified_hosts Parameter, exec_map_timeout Parameter, Global Parameters +@node forced_unmounts Parameter, full_os Parameter, exec_map_timeout Parameter, Global Parameters +@comment node-name, next, previous, up +@subsection @t{forced_unmounts} Parameter +@cindex forced_unmounts Parameter + +(type=boolean, default=@samp{no}). +Sometimes, mount points are hung due to unrecoverable conditions, such +as when NFS servers migrate, change their IP address, are down +permanently, or due to hardware failures, and more. In this case, +attempting to unmount an existing mount point, or even just to +@b{stat}(2) it, results in one of three fatal errors: EIO, ESTALE, or +EBUSY. At that point, @i{Amd} can do little to recover that hung +point (in fact, the OS cannot automatically recover either). For that +reason, some OSs support special kinds of forced unmounts, which must +be used very carefully: they will force an ummount immediately (or +lazily on Linux), which could result in application data loss. +However, that may be the only way to recover the entire host (without +rebooting). Once a hung mount point is forced out, @i{Amd} can then +re-mount a replacement one (if available), bringing a mostly-hung +system back to operation and avoiding a potentially costly reboot. + +If the @samp{forced_unmounts} option is set to @samp{yes}, and the +client OS supports forced or lazy unmounts, then @i{Amd} will attempt +to use them if it gets any of the three serious error conditions +listed above. Note that @i{Amd} will force the unmount of mount +points that returned EBUSY only for @samp{type:=toplvl} mounts +(@xref{Top-level Filesystem}): that is, @i{Amd}'s own mount points. +This is useful to recover from a hung @i{Amd}. + +If this option is set to @samp{no} (the default), then @i{Amd} will +not attempt this special recovery procedure. + +@c ---------------------------------------------------------------- +@node full_os Parameter, fully_qualified_hosts Parameter, forced_unmounts Parameter, Global Parameters @comment node-name, next, previous, up @subsection @t{full_os} Parameter @cindex full_os Parameter @@ -4941,7 +4975,7 @@ the @b{statfs}(2) system call). @subsection @t{unmount_on_exit} Parameter @cindex unmount_on_exit Parameter -(type=boolean), default=@samp{no}). If @samp{yes}, then @i{Amd} will attempt +(type=boolean, default=@samp{no}). If @samp{yes}, then @i{Amd} will attempt to unmount all file systems which it knows about. Normally it leaves all (esp. NFS) mounted file systems intact. Note that @i{Amd} does not know about file systems mounted before it starts up, unless the diff --git a/include/am_utils.h b/include/am_utils.h index 571aac4..97b82e1 100644 --- a/include/am_utils.h +++ b/include/am_utils.h @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: am_utils.h,v 1.66 2005/07/07 23:34:23 ezk Exp $ + * $Id: am_utils.h,v 1.67 2005/07/20 03:32:31 ezk Exp $ * */ @@ -139,10 +139,13 @@ * Systems which have the mount table in a file need to read it before * they can perform an unmount() system call. */ -#define UMOUNT_FS(dir, mtb_name, on_autofs) umount_fs(dir, mtb_name, on_autofs) - +#define UMOUNT_FS(dir, mtb_name, unmount_flags) umount_fs(dir, mtb_name, unmount_flags) /* imported via $srcdir/conf/umount/umount_*.c */ -extern int umount_fs(char *mntdir, const char *mnttabname, int on_autofs); +extern int umount_fs(char *mntdir, const char *mnttabname, int unmount_flags); +/* unmount-related flags (special handling of autofs, forced/lazy, etc.) */ +#define AMU_UMOUNT_FORCE 0x1 +#define AMU_UMOUNT_DETACH 0x2 +#define AMU_UMOUNT_AUTOFS 0x4 /* * The following values can be tuned... diff --git a/m4/macros/header_templates.m4 b/m4/macros/header_templates.m4 index 210f799..f2f3df5 100644 --- a/m4/macros/header_templates.m4 +++ b/m4/macros/header_templates.m4 @@ -412,6 +412,12 @@ AH_TEMPLATE([MNT2_GEN_OPT_CACHE], AH_TEMPLATE([MNT2_GEN_OPT_DATA], [6-argument mount]) +AH_TEMPLATE([MNT2_GEN_OPT_DETACH], +[Use a lazy unmount (detach)]) + +AH_TEMPLATE([MNT2_GEN_OPT_FORCE], +[Use a forced unmount]) + AH_TEMPLATE([MNT2_GEN_OPT_FSS], [old (4-argument) mount (compatibility)]) diff --git a/scripts/amd.conf-sample b/scripts/amd.conf-sample index 5ece23a..cc43fa0 100644 --- a/scripts/amd.conf-sample +++ b/scripts/amd.conf-sample @@ -14,6 +14,7 @@ pid_file = /dev/stdout | /var/run/amd.pid # (amd -r) restart_mounts = no | yes unmount_on_exit = no | yes +forced_unmounts = no | yes # (amd -a) auto_dir = /a # duration in seconds that a looked up name remain cached (amd -c) diff --git a/scripts/amd.conf.5 b/scripts/amd.conf.5 index e0654f7..141c5a0 100644 --- a/scripts/amd.conf.5 +++ b/scripts/amd.conf.5 @@ -38,7 +38,7 @@ .\" .\" %W% (Berkeley) %G% .\" -.\" $Id: amd.conf.5,v 1.37 2005/04/17 03:05:54 ezk Exp $ +.\" $Id: amd.conf.5,v 1.38 2005/07/20 03:32:31 ezk Exp $ .\" .TH AMD.CONF 5 "7 August 1997" .SH NAME @@ -280,6 +280,21 @@ because during the time that the executable map program is queried, .I Amd is essentially waiting and is thus not responding to any other queries. +.TP +.BR forced_unmounts " (boolean, default=no)" +If set to "yes," and the client OS supports forced or lazy unmounts, then +.I Amd +will attempt to use them if it gets any of three serious error conditions +when trying to unmount an existing mount point or mount on top of one: EIO, +ESTALE, or EBUSY. + +This could be useful to recover from serious conditions such as hardware +failure of mounted disks, or NFS servers which are down permanently, were +migrated, or changed their IP address. Only "type:=toplvl" mounts hung with +EBUSY are forcibly unmounted using this option, which is useful to recover +from a hung +.IR Amd ). + .TP .BR full_os " (string, default to compiled in value)" The full name of the operating system, along with its version. Allows you