From abfbbf055df80a53b89e7f26dc478d4876c9e075 Mon Sep 17 00:00:00 2001 From: Erez Zadok Date: Thu, 21 Jul 2005 05:22:47 +0000 Subject: [PATCH] * conf/umount/umount_linux.c (umount_fs): cleanup this function, breaking long "if" statements using "goto out". * conf/umount/umount_{aix,bsd44,osf,default,linux}.c (umount_fs): call new utility function umount2_fs() as needed (EBUSY, EIO, or ESTALE). * conf/umount/umount_{aix,bsd44,osf,default,linux}.c (umount2_fs): define a new utility function to invoke forcible/lazy unmounts without touching any mtab files. This separate utility function is useful because it can be called from elsewhere. * amd/amfs_toplvl.c (amfs_toplvl_init): new function, called before Amd mounts toplvl mounts, which gives us a hook for cleanup of a previously dead Amd. In our case, if the user asked for forced_unmounts, and the OS supports it, then we try forced/lazy unmounts on any previous toplvl mounts. This is useful if a previous Amd died and left behind toplvl mount points (this Amd will clean them up!). WARNING: Don't use forced/lazy unmounts if you have another valid Amd running, because this code WILL force those valid toplvl mount points to be detached as well! * amd/amfs_toplvl.c (amfs_toplvl_umount): don't unconditionally try forced/lazy unmounts because it will prevent a normal Amd from terminating and cleaning up properly (self-deadlocking: detached mounts hang the parent Amd on a stat). Since we already do unmounts in the background, then try a safer policy: after trying the normal unmounts a few times (5 sec), escalate and try forced unmounts a few times (5 more seconds), and if even that failed, then try the ultimate -- detached unmounts (which always succeed). This allows Amd to first try and shutdown cleanly, and gradually try more forcible ways to shutdown. On Linux, this procedure will cleanly shutdown Amd even if there are processes with their CWD on Amd's mount points (which normally result in EBUSY). * Makefile.am (EXTRA_DIST_CONF): add new umount_aix.c to distro. * conf/umount/umount_aix.c: easier if AIX has its own unmount helper file. * m4/macros/check_umount_style.m4: AIX needs its own unmount style file. --- ChangeLog | 45 +++++ Makefile.am | 1 + NEWS | 6 +- amd/amfs_toplvl.c | 62 +++++-- conf/umount/umount_aix.c | 201 +++++++++++++++++++++ conf/umount/umount_bsd44.c | 30 +++- conf/umount/umount_default.c | 37 ++-- conf/umount/umount_linux.c | 297 +++++++++++++++++++------------- conf/umount/umount_osf.c | 30 +++- doc/am-utils.texi | 11 +- include/am_utils.h | 10 +- m4/macros/check_umount_style.m4 | 2 + 12 files changed, 578 insertions(+), 154 deletions(-) create mode 100644 conf/umount/umount_aix.c diff --git a/ChangeLog b/ChangeLog index fb7ec24..77613b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,50 @@ +2005-07-21 Erez Zadok + + * conf/umount/umount_linux.c (umount_fs): cleanup this function, + breaking long "if" statements using "goto out". + + * conf/umount/umount_{aix,bsd44,osf,default,linux}.c (umount_fs): + call new utility function umount2_fs() as needed (EBUSY, EIO, or + ESTALE). + + * conf/umount/umount_{aix,bsd44,osf,default,linux}.c (umount2_fs): + define a new utility function to invoke forcible/lazy unmounts + without touching any mtab files. This separate utility function + is useful because it can be called from elsewhere. + + * amd/amfs_toplvl.c (amfs_toplvl_init): new function, called + before Amd mounts toplvl mounts, which gives us a hook for cleanup + of a previously dead Amd. In our case, if the user asked for + forced_unmounts, and the OS supports it, then we try forced/lazy + unmounts on any previous toplvl mounts. This is useful if a + previous Amd died and left behind toplvl mount points (this Amd + will clean them up!). WARNING: Don't use forced/lazy unmounts if + you have another valid Amd running, because this code WILL force + those valid toplvl mount points to be detached as well! + + * amd/amfs_toplvl.c (amfs_toplvl_umount): don't unconditionally + try forced/lazy unmounts because it will prevent a normal Amd from + terminating and cleaning up properly (self-deadlocking: detached + mounts hang the parent Amd on a stat). Since we already do + unmounts in the background, then try a safer policy: after trying + the normal unmounts a few times (5 sec), escalate and try forced + unmounts a few times (5 more seconds), and if even that failed, + then try the ultimate -- detached unmounts (which always succeed). + This allows Amd to first try and shutdown cleanly, and gradually + try more forcible ways to shutdown. On Linux, this procedure will + cleanly shutdown Amd even if there are processes with their CWD on + Amd's mount points (which normally result in EBUSY). + 2005-07-20 Erez Zadok + * Makefile.am (EXTRA_DIST_CONF): add new umount_aix.c to distro. + + * conf/umount/umount_aix.c: easier if AIX has its own unmount + helper file. + + * m4/macros/check_umount_style.m4: AIX needs its own unmount style + file. + * doc/am-utils.texi (forced_unmounts Parameter): @xref -> @pxref. 2005-07-19 Erez Zadok diff --git a/Makefile.am b/Makefile.am index 23dfa1f..238a14c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -261,6 +261,7 @@ EXTRA_DIST_CONF = \ conf/trap/trap_svr4.h \ conf/trap/trap_ultrix.h \ \ + conf/umount/umount_aix.c \ conf/umount/umount_bsd44.c \ conf/umount/umount_default.c \ conf/umount/umount_linux.c \ diff --git a/NEWS b/NEWS index 10cfa53..0f1b260 100644 --- a/NEWS +++ b/NEWS @@ -8,7 +8,11 @@ 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). +option: this is useful to ensure that a new Amd can mount itself even if a +previous Amd died and left its mount points hung, or to force Amd to +shutdown cleanly, even if some processes (i.e., user shells) have their CWD +on Amd's own mount point. This functionality is available for Linux, BSD44 +systems, Solaris, OSF/1, and partially for AIX. - minor new ports: i386-pc-linux-fc4 diff --git a/amd/amfs_toplvl.c b/amd/amfs_toplvl.c index be7daf7..cfc5e7e 100644 --- a/amd/amfs_toplvl.c +++ b/amd/amfs_toplvl.c @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: amfs_toplvl.c,v 1.39 2005/07/20 03:32:30 ezk Exp $ + * $Id: amfs_toplvl.c,v 1.40 2005/07/21 05:22:47 ezk Exp $ * */ @@ -54,7 +54,7 @@ /**************************************************************************** *** FORWARD DEFINITIONS *** ****************************************************************************/ - +static int amfs_toplvl_init(mntfs *mf); /**************************************************************************** *** OPS STRUCTURES *** @@ -63,7 +63,7 @@ am_ops amfs_toplvl_ops = { "toplvl", amfs_generic_match, - 0, /* amfs_toplvl_init */ + amfs_toplvl_init, /* amfs_toplvl_init */ amfs_toplvl_mount, amfs_toplvl_umount, amfs_generic_lookup_child, @@ -133,6 +133,36 @@ set_auto_attrcache_timeout(char *preopts, char *opts) } +/* + * Initialize a top-level mount. In our case, if the user asked for + * forced_unmounts, and the OS supports it, then we try forced/lazy unmounts + * on any previous toplvl mounts. This is useful if a previous Amd died and + * left behind toplvl mount points (this Amd will clean them up). + * + * WARNING: Don't use forced/lazy unmounts if you have another valid Amd + * running, because this code WILL force those valid toplvl mount points to + * be detached as well! + */ +static int +amfs_toplvl_init(mntfs *mf) +{ + int error = 0; + +#if defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH) + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + plog(XLOG_INFO, "amfs_toplvl_init: trying forced/lazy unmount of %s", + mf->mf_mount); + error = umount2_fs(mf->mf_mount, AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH); + if (error) + plog(XLOG_INFO, "amfs_toplvl_init: forced/lazy unmount failed: %m"); + else + dlog("amfs_toplvl_init: forced/lazy unmount succeeded"); + } +#endif /* MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH */ + return error; +} + + /* * Mount the top-level */ @@ -221,12 +251,7 @@ amfs_toplvl_umount(am_node *mp, mntfs *mf) struct stat stb; 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); - } + int count = 0; /* how many times did we try to unmount? */ again: /* @@ -263,7 +288,24 @@ again: return error; #endif /* HAVE_FS_AUTOFS */ plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path); - sleep(1); /* XXX */ + count++; + sleep(1); + /* + * If user wants forced/lazy unmount semantics, then set those flags, + * but only after we've tried normal lstat/umount a few times -- + * otherwise forced unmounts may hang this very same Amd (by preventing + * it from achieving a clean unmount). + */ + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + if (count == 5) { /* after 5 seconds, try MNT_FORCE */ + dlog("enabling forced unmounts for toplvl node %s", mp->am_path); + unmount_flags |= AMU_UMOUNT_FORCE; + } + if (count == 10) { /* after 10 seconds, try MNT_DETACH */ + dlog("enabling detached unmounts for toplvl node %s", mp->am_path); + unmount_flags |= AMU_UMOUNT_DETACH; + } + } goto again; } out: diff --git a/conf/umount/umount_aix.c b/conf/umount/umount_aix.c new file mode 100644 index 0000000..f0881ed --- /dev/null +++ b/conf/umount/umount_aix.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 1997-2005 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * $Id: umount_aix.c,v 1.1 2005/07/21 05:22:47 ezk Exp $ + * + */ + +/* + * AIX method of unmounting filesystems. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ +#include +#include + + +int +umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) +{ + mntlist *mlist, *mp, *mp_save = 0; + int error = 0; + + mp = mlist = read_mtab(mntdir, mnttabname); + + /* + * Search the mount table looking for + * the correct (ie last) matching entry + */ + while (mp) { + if (STREQ(mp->mnt->mnt_dir, mntdir)) + mp_save = mp; + mp = mp->mnext; + } + + if (mp_save) { + dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir); + +#ifdef MOUNT_TABLE_ON_FILE + /* + * This unmount may hang leaving this process with an exclusive lock on + * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount, + * then lock mtab (again) and reread it and finally update it. + */ + unlock_mntlist(); +#endif /* MOUNT_TABLE_ON_FILE */ + +#ifdef NEED_AUTOFS_SPACE_HACK + 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); + XFREE(mp_save->mnt->mnt_dir); + mp_save->mnt->mnt_dir = mnt_dir_save; + } else +#endif /* NEED_AUTOFS_SPACE_HACK */ + error = UNMOUNT_TRAP(mp_save->mnt); + if (error < 0) { + switch ((error = errno)) { + case EINVAL: + case ENOTBLK: + plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); + error = 0; /* Not really an error */ + break; + + case ENOENT: + /* + * This could happen if the kernel insists on following symlinks + * when we try to unmount a direct mountpoint. We need to propagate + * the error up so that the top layers know it failed and don't + * try to rmdir() the mountpoint or other silly things. + */ + plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); + break; + +#if defined(MNT2_GEN_OPT_FORCE) && defined(HAVE_UVMOUNT) + case EBUSY: + case EIO: + case ESTALE: + /* caller determines if forced unmounts should be used */ + if (unmount_flags & AMU_UMOUNT_FORCE) { + error = umount2_fs(mntdir, unmount_flags); + if (error < 0) + error = errno; + else + break; /* all is OK */ + } + /* fallthrough */ +#endif /* MNT2_GEN_OPT_FORCE && HAVE_UVMOUNT */ + default: + dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); + break; + } + } + dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir); + + if (!error) { +#ifdef MOUNT_TABLE_ON_FILE + free_mntlist(mlist); + mp = mlist = read_mtab(mntdir, mnttabname); + + /* + * Search the mount table looking for + * the correct (ie last) matching entry + */ + mp_save = 0; + while (mp) { + if (STREQ(mp->mnt->mnt_dir, mntdir)) + mp_save = mp; + mp = mp->mnext; + } + + if (mp_save) { + mnt_free(mp_save->mnt); + mp_save->mnt = 0; + rewrite_mtab(mlist, mnttabname); + } +#endif /* MOUNT_TABLE_ON_FILE */ + } + + } else { + + plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir); + /* + * Assume it is already unmounted + */ + error = 0; + } /* end of "if (mp_save)" statement */ + + free_mntlist(mlist); + + return error; +} + + +#if defined(MNT2_GEN_OPT_FORCE) && defined(HAVE_UVMOUNT) +/* force unmount, no questions asked, without touching mnttab file */ +int +umount2_fs(const char *mntdir, u_int unmount_flags) +{ + int error = 0; +#if 0 + u_int vfs_id = 0; +#endif + + if (unmount_flags & AMU_UMOUNT_FORCE) { + plog(XLOG_INFO, "**UNIMPLEMENTED**: umount2_fs: trying unmount/forced on %s", mntdir); +#if 0 + /* + * XXX: need to implement. Call read_mtab and search mntlist for VFS ID + * of mntdir, then pass that ID to uvmount(), then call free_mntlist(). + */ + error = uvmount(vfs_id, MNT2_GEN_OPT_FORCE); /* AIX */ + if (error < 0 && (errno == EINVAL || errno == ENOENT)) + error = 0; /* ignore EINVAL/ENOENT */ + if (error < 0) + plog(XLOG_WARNING, "%u: unmount/force: %m", vfs_id); + else + dlog("%s: unmount/force: OK", mntdir); +#endif + } + return error; +} +#endif /* MNT2_GEN_OPT_FORCE && HAVE_UVMOUNT */ diff --git a/conf/umount/umount_bsd44.c b/conf/umount/umount_bsd44.c index 05ca413..c2ac091 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.13 2005/07/20 03:32:30 ezk Exp $ + * $Id: umount_bsd44.c,v 1.14 2005/07/21 05:22:47 ezk Exp $ * */ @@ -53,7 +53,7 @@ int -umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) +umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) { int error; @@ -81,10 +81,11 @@ eintr: 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 = umount2_fs(mntdir, unmount_flags); + if (error < 0) error = errno; else - return error; + return error; } /* fallthrough */ #endif /* MNT2_GEN_OPT_FORCE */ @@ -96,3 +97,24 @@ eintr: return error; } + + +#ifdef MNT2_GEN_OPT_FORCE +/* force unmount, no questions asked, without touching mnttab file */ +int +umount2_fs(const char *mntdir, u_int unmount_flags) +{ + int error = 0; + if (unmount_flags & AMU_UMOUNT_FORCE) { + plog(XLOG_INFO, "umount2_fs: trying unmount/forced on %s", mntdir); + error = unmount(mntdir, MNT2_GEN_OPT_FORCE); + if (error < 0 && (errno == EINVAL || errno == ENOENT)) + error = 0; /* ignore EINVAL/ENOENT */ + if (error < 0) + plog(XLOG_WARNING, "%s: unmount/force: %m", mntdir); + else + dlog("%s: unmount/force: OK", mntdir); + } + return error; +} +#endif /* MNT2_GEN_OPT_FORCE */ diff --git a/conf/umount/umount_default.c b/conf/umount/umount_default.c index 817d0dc..f770542 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.15 2005/07/20 03:32:30 ezk Exp $ + * $Id: umount_default.c,v 1.16 2005/07/21 05:22:47 ezk Exp $ * */ @@ -53,7 +53,7 @@ int -umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) +umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) { mntlist *mlist, *mp, *mp_save = 0; int error = 0; @@ -110,26 +110,20 @@ umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) 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)) +#if defined(MNT2_GEN_OPT_FORCE) && defined(HAVE_UMOUNT2) 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 */ + error = umount2_fs(mntdir, unmount_flags); if (error < 0) error = errno; else break; /* all is OK */ } /* fallthrough */ -#endif /* MNT2_GEN_OPT_FORCE && (HAVE_UMOUNT2 || HAVE_UVMOUNT) */ +#endif /* MNT2_GEN_OPT_FORCE && HAVE_UMOUNT2 */ default: dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); break; @@ -174,3 +168,24 @@ umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) return error; } + + +#if defined(MNT2_GEN_OPT_FORCE) && defined(HAVE_UMOUNT2) +/* force unmount, no questions asked, without touching mnttab file */ +int +umount2_fs(const char *mntdir, u_int unmount_flags) +{ + int error = 0; + if (unmount_flags & AMU_UMOUNT_FORCE) { + plog(XLOG_INFO, "umount2_fs: trying unmount/forced on %s", mntdir); + error = umount2(mntdir, MNT2_GEN_OPT_FORCE); /* Solaris */ + if (error < 0 && (errno == EINVAL || errno == ENOENT)) + error = 0; /* ignore EINVAL/ENOENT */ + if (error < 0) + plog(XLOG_WARNING, "%s: unmount/force: %m", mntdir); + else + dlog("%s: unmount/force: OK", mntdir); + } + return error; +} +#endif /* MNT2_GEN_OPT_FORCE && HAVE_UMOUNT2 */ diff --git a/conf/umount/umount_linux.c b/conf/umount/umount_linux.c index f4b6e04..041b677 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.6 2005/07/20 03:32:30 ezk Exp $ + * $Id: umount_linux.c,v 1.7 2005/07/21 05:22:47 ezk Exp $ * */ @@ -54,11 +54,15 @@ int -umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) +umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) { - struct stat dummy; mntlist *mlist, *mp, *mp_save = 0; int error = 0; +#ifdef HAVE_LOOP_DEVICE + char *opt, *xopts=NULL; + char loopstr[] = "loop="; + char *loopdev; +#endif /* HAVE_LOOP_DEVICE */ mp = mlist = read_mtab(mntdir, mnttabname); @@ -72,143 +76,202 @@ umount_fs(char *mntdir, const char *mnttabname, int unmount_flags) mp = mp->mnext; } - if (mp_save) { - dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir); + if (!mp_save) { + plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir); + /* Assume it is already unmounted */ + error = 0; + goto out; + } + + dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir); #ifdef MOUNT_TABLE_ON_FILE - /* - * This unmount may hang leaving this process with an exclusive lock on - * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount, - * then lock mtab (again) and reread it and finally update it. - */ - unlock_mntlist(); + /* + * This unmount may hang leaving this process with an exclusive lock on + * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount, + * then lock mtab (again) and reread it and finally update it. + */ + unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ #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. + */ + if (unmount_flags & AMU_UMOUNT_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. + * Note: we pass both DETACH and FORCE flags, because umount2_fs below + * (on Linux), should try FORCE before DETACH (the latter always + * succeeds). */ - 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 + error = umount2_fs(mp_save->mnt->mnt_dir, + unmount_flags & (AMU_UMOUNT_DETACH|AMU_UMOUNT_FORCE)); + } 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)) { - case EINVAL: - case ENOTBLK: - plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); - error = 0; /* Not really an error */ - break; - - case ENOENT: - /* - * This could happen if the kernel insists on following symlinks - * when we try to unmount a direct mountpoint. We need to propagate - * the error up so that the top layers know it failed and don't - * try to rmdir() the mountpoint or other silly things. - */ - plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); - break; + error = UNMOUNT_TRAP(mp_save->mnt); + if (error < 0) { + plog(XLOG_WARNING, "unmount(%s) failed: %m", mp_save->mnt->mnt_dir); + switch ((error = errno)) { + case EINVAL: + case ENOTBLK: + plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); + error = 0; /* Not really an error */ + break; -#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 /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) */ + case ENOENT: + /* + * This could happen if the kernel insists on following symlinks + * when we try to unmount a direct mountpoint. We need to propagate + * the error up so that the top layers know it failed and don't + * try to rmdir() the mountpoint or other silly things. + */ + plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); + break; - default: - dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); - break; +#if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) + 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_fs(mp_save->mnt->mnt_dir, + unmount_flags & AMU_UMOUNT_FORCE); + if (error < 0) { + plog(XLOG_WARNING, "%s: unmount/force: %m", + mp_save->mnt->mnt_dir); + error = errno; + } } - } else { - dlog("unmount(%s) succeeded", mp_save->mnt->mnt_dir); + break; +#endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) */ + + default: + dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); + break; } - dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir); + } else { + dlog("unmount(%s) succeeded", mp_save->mnt->mnt_dir); + } + dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir); + + /* + * If we are successful or there was an ENOENT, remove + * the mount entry from the mtab file. + */ + if (error && error != ENOENT) + goto out; - /* - * If we are successful or there was an ENOENT, remove - * the mount entry from the mtab file. - */ - if (!error || error == ENOENT) { #ifdef HAVE_LOOP_DEVICE - /* look for loop=/dev/loopX in mnt_opts */ - char *opt, *xopts=NULL; - char loopstr[] = "loop="; - char *loopdev; - xopts = strdup(mp_save->mnt->mnt_opts); /* b/c strtok is destructive */ - for (opt = strtok(xopts, ","); opt; opt = strtok(NULL, ",")) - if (NSTREQ(opt, loopstr, sizeof(loopstr) - 1)) { - loopdev = opt + sizeof(loopstr) - 1; - if (delete_loop_device(loopdev) < 0) - plog(XLOG_WARNING, "unmount() failed to release loop device %s: %m", loopdev); - else - plog(XLOG_INFO, "unmount() released loop device %s OK", loopdev); - break; - } - if (xopts) - XFREE(xopts); + /* look for loop=/dev/loopX in mnt_opts */ + xopts = strdup(mp_save->mnt->mnt_opts); /* b/c strtok is destructive */ + for (opt = strtok(xopts, ","); opt; opt = strtok(NULL, ",")) + if (NSTREQ(opt, loopstr, sizeof(loopstr) - 1)) { + loopdev = opt + sizeof(loopstr) - 1; + if (delete_loop_device(loopdev) < 0) + plog(XLOG_WARNING, "unmount() failed to release loop device %s: %m", loopdev); + else + plog(XLOG_INFO, "unmount() released loop device %s OK", loopdev); + break; + } + if (xopts) + XFREE(xopts); #endif /* HAVE_LOOP_DEVICE */ #ifdef MOUNT_TABLE_ON_FILE - free_mntlist(mlist); - mp = mlist = read_mtab(mntdir, mnttabname); + free_mntlist(mlist); + mp = mlist = read_mtab(mntdir, mnttabname); - /* - * Search the mount table looking for - * the correct (ie last) matching entry - */ - mp_save = 0; - while (mp) { - if (STREQ(mp->mnt->mnt_dir, mntdir)) - mp_save = mp; - mp = mp->mnext; - } + /* + * Search the mount table looking for + * the correct (ie last) matching entry + */ + mp_save = 0; + while (mp) { + if (STREQ(mp->mnt->mnt_dir, mntdir)) + mp_save = mp; + mp = mp->mnext; + } - if (mp_save) { - mnt_free(mp_save->mnt); - mp_save->mnt = 0; - rewrite_mtab(mlist, mnttabname); - } + if (mp_save) { + mnt_free(mp_save->mnt); + mp_save->mnt = 0; + rewrite_mtab(mlist, mnttabname); + } #endif /* MOUNT_TABLE_ON_FILE */ - } - } else { + out: + free_mntlist(mlist); - plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir); - /* - * Assume it is already unmounted - */ - error = 0; - } /* end of "if (mp_save)" statement */ + return error; +} - free_mntlist(mlist); +#if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) +/* + * Force unmount, no questions asked, without touching mnttab file. + * The order here is relevant because we may want to try the "safer" detach + * unmount before trying the more drastic "forced" unmount. + */ +int +umount2_fs(const char *mntdir, u_int unmount_flags) +{ + int error = 0; + +#ifdef MNT2_GEN_OPT_FORCE + if (unmount_flags & AMU_UMOUNT_FORCE) { + plog(XLOG_INFO, "umount2_fs: trying unmount/forced on %s", mntdir); + error = umount2(mntdir, MNT2_GEN_OPT_FORCE); + if (error < 0 && (errno == EINVAL || errno == ENOENT)) + error = 0; /* ignore EINVAL/ENOENT */ + if (error < 0) + plog(XLOG_WARNING, "%s: unmount/force: %m", mntdir); + else { + dlog("%s: unmount/force: OK", mntdir); + goto out; + } + } +#endif /* MNT2_GEN_OPT_FORCE */ + +#ifdef MNT2_GEN_OPT_DETACH + /* + * 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) { + struct stat dummy; + dlog("umount_fs: try stat() before unmount/detach"); + error = stat(mntdir, &dummy); + if (!error || (errno == ESTALE || errno == EIO)) { + if (error < 0) + plog(XLOG_INFO, "unmount2_fs: trying unmount/detach of %s (%m)", + mntdir); + else + plog(XLOG_INFO, "unmount2_fs: trying unmount/detach of %s", + mntdir); + error = umount2(mntdir, MNT2_GEN_OPT_DETACH); + if (error < 0 && (errno == EINVAL || errno == ENOENT)) + error = 0; /* ignore EINVAL/ENOENT */ + if (error < 0) /* don't try FORCE if detach succeeded */ + plog(XLOG_WARNING, "%s: unmount/detach: %m", mntdir); + else { + dlog("%s: unmount/detach: OK", mntdir); + goto out; /* superfluous (but symmetric code :-) */ + } + } + } +#endif /* MNT2_GEN_OPT_DETACH */ + +#ifdef MNT2_GEN_OPT_DETACH + out: +#endif /* MNT2_GEN_OPT_DETACH */ return error; } +#endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */ diff --git a/conf/umount/umount_osf.c b/conf/umount/umount_osf.c index 57719a9..d6e9abb 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.12 2005/07/20 03:32:30 ezk Exp $ + * $Id: umount_osf.c,v 1.13 2005/07/21 05:22:47 ezk Exp $ * */ @@ -53,7 +53,7 @@ int -umount_fs(char *fs_name, const char *mnttabname, int unmount_flags) +umount_fs(char *fs_name, const char *mnttabname, u_int unmount_flags) { int error; @@ -84,10 +84,11 @@ eintr: 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 = umount2_fs(mntdir, unmount_flags); + if ((error = umount2_fs(mntdir)) < 0) error = errno; else - return error; + return error; } /* fallthrough */ #endif /* MNT2_GEN_OPT_FORCE */ @@ -99,3 +100,24 @@ eintr: return error; } + + +#ifdef MNT2_GEN_OPT_FORCE +/* force unmount, no questions asked, without touching mnttab file */ +int +umount2_fs(const char *mntdir, u_int unmount_flags) +{ + int error = 0; + if (unmount_flags & AMU_UMOUNT_FORCE) { + plog(XLOG_INFO, "umount2_fs: trying unmount/forced on %s", mntdir); + error = umount(mntdir, MNT2_GEN_OPT_FORCE); + if (error < 0 && (errno == EINVAL || errno == ENOENT)) + error = 0; /* ignore EINVAL/ENOENT */ + if (error < 0) + plog(XLOG_WARNING, "%s: unmount/force: %m", mntdir); + else + dlog("%s: unmount/force: OK", mntdir); + } + return error; +} +#endif /* MNT2_GEN_OPT_FORCE */ diff --git a/doc/am-utils.texi b/doc/am-utils.texi index 674e400..05a3ae4 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.104 2005/07/21 00:50:02 ezk Exp $ +@c $Id: am-utils.texi,v 1.105 2005/07/21 05:22:47 ezk Exp $ @c @setfilename am-utils.info @@ -658,10 +658,10 @@ value on a per-mount basis (@pxref{opts Option, opts, opts}). Filesystems can be forcefully timed out using the @i{Amq} command. @xref{Run-time Administration}. Note that on new enough systems that -support forced unmounts, such as Linux, @i{Amd} will try to use the +support forced unmounts, such as Linux, @i{Amd} can try to use the @b{umount2}(2) system call to force the unmount, if the regular @b{umount}(2) system call failed in a way that indicates that the -mount point is hung or stale. +mount point is hung or stale. @xref{forced_unmounts Parameter}. @node Keep-alives, Non-blocking Operation, Automatic Unmounting, Overview @comment node-name, next, previous, up @@ -4465,7 +4465,10 @@ 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 (@pxref{Top-level Filesystem}): that is, @i{Amd}'s own mount points. -This is useful to recover from a hung @i{Amd}. +This is useful to recover from a previously hung @i{Amd}, and to +ensure that an existing @i{Amd} can shutdown cleanly even if some +processes are keeping its mount points busy (i.e., when a user's shell +process uses @code{cd} to set its CWD to @i{Amd}'s own mount point). If this option is set to @samp{no} (the default), then @i{Amd} will not attempt this special recovery procedure. diff --git a/include/am_utils.h b/include/am_utils.h index 97b82e1..33c0891 100644 --- a/include/am_utils.h +++ b/include/am_utils.h @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: am_utils.h,v 1.67 2005/07/20 03:32:31 ezk Exp $ + * $Id: am_utils.h,v 1.68 2005/07/21 05:22:47 ezk Exp $ * */ @@ -140,8 +140,12 @@ * they can perform an unmount() system call. */ #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 unmount_flags); +/* next two are imported via $srcdir/conf/umount/umount_*.c */ +extern int umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags); +#ifdef MNT2_GEN_OPT_FORCE +extern int umount2_fs(const char *mntdir, u_int unmount_flags); +#endif /* MNT2_GEN_OPT_FORCE */ + /* unmount-related flags (special handling of autofs, forced/lazy, etc.) */ #define AMU_UMOUNT_FORCE 0x1 #define AMU_UMOUNT_DETACH 0x2 diff --git a/m4/macros/check_umount_style.m4 b/m4/macros/check_umount_style.m4 index 2df0b0a..c5abcf1 100644 --- a/m4/macros/check_umount_style.m4 +++ b/m4/macros/check_umount_style.m4 @@ -13,6 +13,8 @@ case "${host_os_name}" in ac_cv_style_umount=bsd44 ;; osf* ) ac_cv_style_umount=osf ;; + aix* ) + ac_cv_style_umount=aix ;; * ) ac_cv_style_umount=default ;; esac -- 2.43.0