mirror of
https://github.com/OpenRC/openrc.git
synced 2026-01-26 16:29:07 +00:00
do_unmount: add parallel unmounting
this turns mouninfo into a multicall binary which can also do parallel unmounting. most mountinfo code is unchanged; only notable change is find_mounts() now returns the number of mounts found and also takes the "process" function as an argument; so that is_mounted() can be implemented on top of it. do_unmount mostly follows the logic of previous code. some notable changes: - do_unmount is now "lazy" when it comes to retrying failed unmounts. it will greedily keep running unmount as long as it can before it looks at the "waiting" queue. - it will check if the mountpoint is still mounted or not when umount returns non-zero exit code. this is due to the fact that multiple umount calls might race to unmount a shared mount. so if umount fails _but_ the mountpoint is no longer mounted, we assume success. - do_unmount used to fail if fuser did not find any pids using the mount. the new code tries one more time; the rationale being that there's a gap between the umount call and the fuser call, and so whatever was using the mount before might have stopped using it now and so it's worth another attempt. Fixes: https://github.com/OpenRC/openrc/issues/662 Closes: https://github.com/OpenRC/openrc/pull/698
This commit is contained in:
parent
a477d469eb
commit
303bc524e8
@ -112,7 +112,7 @@ stop()
|
||||
# Umount loop devices
|
||||
einfo "Unmounting loop devices"
|
||||
eindent
|
||||
do_unmount "umount -d" --skip-point-regex "$no_umounts_r" \
|
||||
do_unmount -d -- --skip-point-regex "$no_umounts_r" \
|
||||
--node-regex "^/dev/loop"
|
||||
eoutdent
|
||||
|
||||
@ -125,7 +125,7 @@ stop()
|
||||
fs="$fs${fs:+|}$x"
|
||||
done
|
||||
[ -n "$fs" ] && fs="^($fs)$"
|
||||
do_unmount umount --skip-point-regex "$no_umounts_r" \
|
||||
do_unmount -- --skip-point-regex "$no_umounts_r" \
|
||||
"${fs:+--skip-fstype-regex}" $fs --nonetdev
|
||||
eoutdent
|
||||
|
||||
|
||||
@ -25,8 +25,6 @@ start()
|
||||
sync
|
||||
|
||||
ebegin "Remounting remaining filesystems read-only"
|
||||
# We need the do_unmount function
|
||||
. "$RC_LIBEXECDIR"/sh/rc-mount.sh
|
||||
eindent
|
||||
|
||||
# Bug 381783
|
||||
@ -48,7 +46,7 @@ start()
|
||||
fs="$fs${fs:+|}$x"
|
||||
done
|
||||
[ -n "$fs" ] && fs="^($fs)$"
|
||||
do_unmount "umount -r" \
|
||||
do_unmount -r -- \
|
||||
--skip-point-regex "$m" \
|
||||
"${fs:+--skip-fstype-regex}" $fs --nonetdev
|
||||
ret=$?
|
||||
|
||||
@ -64,7 +64,6 @@ stop()
|
||||
local x= fs=
|
||||
|
||||
ebegin "Unmounting network filesystems"
|
||||
. "$RC_LIBEXECDIR"/sh/rc-mount.sh
|
||||
|
||||
for x in $net_fs_list $extra_net_fs_list; do
|
||||
fs="$fs${fs:+,}$x"
|
||||
@ -79,7 +78,7 @@ stop()
|
||||
fs="$fs${fs:+|}$x"
|
||||
done
|
||||
[ -n "$fs" ] && fs="^($fs)$"
|
||||
do_unmount umount ${fs:+--fstype-regex} $fs --netdev
|
||||
do_unmount -- ${fs:+--fstype-regex} $fs --netdev
|
||||
retval=$?
|
||||
|
||||
eoutdent
|
||||
|
||||
@ -10,7 +10,6 @@ sh_conf_data.set('UUCP_GROUP', get_option('uucp_group'))
|
||||
|
||||
sh = [
|
||||
'rc-functions.sh',
|
||||
'rc-mount.sh',
|
||||
'runit.sh',
|
||||
's6.sh',
|
||||
'start-stop-daemon.sh',
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
# Copyright (c) 2007-2015 The OpenRC Authors.
|
||||
# See the Authors file at the top-level directory of this distribution and
|
||||
# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
|
||||
#
|
||||
# This file is part of OpenRC. It is subject to the license terms in
|
||||
# the LICENSE file found in the top-level directory of this
|
||||
# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
|
||||
# This file may not be copied, modified, propagated, or distributed
|
||||
# except according to the terms contained in the LICENSE file.
|
||||
|
||||
# Declare this here so that no formatting doesn't affect the embedded newline
|
||||
__IFS="
|
||||
"
|
||||
|
||||
# Handy function to handle all our unmounting needs
|
||||
# mountinfo is a C program to actually find our mounts on our supported OS's
|
||||
# We rely on fuser being present, so if it's not then don't unmount anything.
|
||||
# This isn't a real issue for the BSD's, but it is for Linux.
|
||||
do_unmount()
|
||||
{
|
||||
local cmd="$1" retval=0 retry= pids=-
|
||||
local f_opts="-m -c" f_kill="-s " mnt=
|
||||
if [ "$RC_UNAME" = "Linux" ]; then
|
||||
f_opts="-m"
|
||||
f_kill="-"
|
||||
fi
|
||||
|
||||
shift
|
||||
local IFS="$__IFS"
|
||||
set -- $(mountinfo "$@")
|
||||
unset IFS
|
||||
for mnt; do
|
||||
# Unmounting a shared mount can unmount other mounts, so
|
||||
# we need to check the mount is still valid
|
||||
mountinfo --quiet "$mnt" || continue
|
||||
# Ensure we interpret all characters properly.
|
||||
mnt=$(printf "$mnt")
|
||||
|
||||
case "$cmd" in
|
||||
umount)
|
||||
ebegin "Unmounting $mnt"
|
||||
;;
|
||||
*)
|
||||
ebegin "Remounting $mnt read only"
|
||||
;;
|
||||
esac
|
||||
|
||||
retry=4 # Effectively TERM, sleep 1, TERM, sleep 1, KILL, sleep 1
|
||||
while ! LC_ALL=C $cmd "$mnt" 2>/dev/null; do
|
||||
if command -v fuser >/dev/null 2>&1; then
|
||||
pids="$(timeout -s KILL "${rc_fuser_timeout:-60}" \
|
||||
fuser $f_opts "$mnt" 2>/dev/null)"
|
||||
fi
|
||||
case " $pids " in
|
||||
*" $$ "*)
|
||||
eend 1 "failed because we are using" \
|
||||
"$mnt"
|
||||
retry=0;;
|
||||
" - ")
|
||||
eend 1
|
||||
retry=0;;
|
||||
" ")
|
||||
eend 1 "in use but fuser finds nothing"
|
||||
retry=0;;
|
||||
*)
|
||||
if [ $retry -le 0 ]; then
|
||||
eend 1
|
||||
else
|
||||
local sig="TERM"
|
||||
: $(( retry -= 1 ))
|
||||
[ $retry = 1 ] && sig="KILL"
|
||||
fuser $f_kill$sig -k $f_opts \
|
||||
"$mnt" >/dev/null 2>&1
|
||||
sleep 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
[ $retry -le 0 ] && break
|
||||
done
|
||||
if [ $retry -le 0 ]; then
|
||||
retval=1
|
||||
else
|
||||
eend 0
|
||||
fi
|
||||
done
|
||||
return $retval
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
executable('mountinfo', 'mountinfo.c',
|
||||
include_directories: incdir,
|
||||
dependencies: [rc, einfo, shared],
|
||||
install: true,
|
||||
install_dir: rc_bindir)
|
||||
foreach exec : ['mountinfo', 'do_unmount']
|
||||
executable(exec, 'mountinfo.c',
|
||||
include_directories: incdir,
|
||||
dependencies: [rc, einfo, shared],
|
||||
install: true,
|
||||
install_dir: rc_bindir)
|
||||
endforeach
|
||||
|
||||
@ -48,6 +48,8 @@
|
||||
#include "einfo.h"
|
||||
#include "queue.h"
|
||||
#include "rc.h"
|
||||
#include "rc_exec.h"
|
||||
#include "timeutils.h"
|
||||
#include "_usage.h"
|
||||
#include "helpers.h"
|
||||
|
||||
@ -89,6 +91,21 @@ const char * const longopts_help[] = {
|
||||
};
|
||||
const char *usagestring = NULL;
|
||||
|
||||
#define UMOUNT_ARGS_MAX 16
|
||||
#define RUN_MAX 32
|
||||
#define TRY_MAX 3
|
||||
#define TRY_DELAY_MS 1000
|
||||
|
||||
struct run_queue {
|
||||
const char *mntpath;
|
||||
int64_t last_exec_time;
|
||||
int64_t fuser_exec_time;
|
||||
pid_t pid;
|
||||
pid_t fuser_pid;
|
||||
int try_count;
|
||||
int fuser_stdoutfd;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
mount_from,
|
||||
mount_to,
|
||||
@ -102,7 +119,12 @@ typedef enum {
|
||||
net_no
|
||||
} net_opts;
|
||||
|
||||
struct args;
|
||||
typedef int process_func_t(RC_STRINGLIST *, struct args *,
|
||||
char *, char *, char *, char *, int);
|
||||
|
||||
struct args {
|
||||
process_func_t *process;
|
||||
regex_t *node_regex;
|
||||
regex_t *skip_node_regex;
|
||||
regex_t *fstype_regex;
|
||||
@ -112,6 +134,7 @@ struct args {
|
||||
RC_STRINGLIST *mounts;
|
||||
mount_type mount_type;
|
||||
net_opts netdev;
|
||||
const char *check_mntpath;
|
||||
};
|
||||
|
||||
static int
|
||||
@ -243,7 +266,7 @@ static struct opt {
|
||||
};
|
||||
|
||||
static RC_STRINGLIST *
|
||||
find_mounts(struct args *args)
|
||||
find_mounts(struct args *args, size_t *num_mounts)
|
||||
{
|
||||
struct statfs *mnts;
|
||||
int nmnts;
|
||||
@ -277,7 +300,7 @@ find_mounts(struct args *args)
|
||||
flags &= ~o->o_opt;
|
||||
}
|
||||
|
||||
process_mount(list, args,
|
||||
*num_mounts += 0 == args->process(list, args,
|
||||
mnts[i].f_mntfromname,
|
||||
mnts[i].f_mntonname,
|
||||
mnts[i].f_fstypename,
|
||||
@ -310,7 +333,7 @@ getmntfile(const char *file)
|
||||
}
|
||||
|
||||
static RC_STRINGLIST *
|
||||
find_mounts(struct args *args)
|
||||
find_mounts(struct args *args, size_t *num_mounts)
|
||||
{
|
||||
FILE *fp;
|
||||
char *buffer;
|
||||
@ -345,7 +368,9 @@ find_mounts(struct args *args)
|
||||
netdev = 1;
|
||||
}
|
||||
|
||||
process_mount(list, args, from, to, fst, opts, netdev);
|
||||
*num_mounts += 0 == args->process(list, args,
|
||||
from, to, fst, opts, netdev);
|
||||
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
@ -359,6 +384,157 @@ find_mounts(struct args *args)
|
||||
# error "Operating system not supported!"
|
||||
#endif
|
||||
|
||||
static int is_prefix(const char *needle, const char *hay)
|
||||
{
|
||||
size_t nlen = strlen(needle);
|
||||
if (strncmp(needle, hay, nlen) == 0 && hay[nlen] == '/')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static char *unescape_octal(char *beg)
|
||||
{
|
||||
int n, i;
|
||||
char *w = beg, *r = beg;
|
||||
while (*r) {
|
||||
if (*r != '\\' || *++r == '\\') {
|
||||
*w++ = *r++;
|
||||
} else {
|
||||
/* octal. should have at least 3 bytes,
|
||||
* but don't choke on malformed input
|
||||
*/
|
||||
for (i = n = 0; i < 3; ++i) {
|
||||
if (*r >= '0' && *r <= '7') {
|
||||
n <<= 3;
|
||||
n |= *r++ - '0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n)
|
||||
*w++ = n;
|
||||
}
|
||||
}
|
||||
*w = '\0';
|
||||
return beg;
|
||||
}
|
||||
|
||||
static int check_is_mounted(RC_STRINGLIST *list RC_UNUSED, struct args *args,
|
||||
char *from RC_UNUSED, char *to, char *fstype RC_UNUSED,
|
||||
char *options RC_UNUSED, int netdev RC_UNUSED)
|
||||
{
|
||||
return strcmp(args->check_mntpath, unescape_octal(to)) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
static int is_mounted(const char *mntpath)
|
||||
{
|
||||
size_t num_mounts = 0;
|
||||
struct args args = { .process = check_is_mounted, .check_mntpath = mntpath };
|
||||
RC_STRINGLIST *l = find_mounts(&args, &num_mounts);
|
||||
rc_stringlist_free(l);
|
||||
return num_mounts > 0;
|
||||
}
|
||||
|
||||
static pid_t run_umount(const char *mntpath,
|
||||
const char **umount_args, int umount_args_num)
|
||||
{
|
||||
struct exec_args args;
|
||||
struct exec_result res;
|
||||
const char *argv[UMOUNT_ARGS_MAX + 3];
|
||||
int k, i = 0;
|
||||
|
||||
argv[i++] = "umount";
|
||||
for (k = 0; k < umount_args_num; ++k)
|
||||
argv[i++] = umount_args[k];
|
||||
argv[i++] = mntpath;
|
||||
argv[i++] = NULL;
|
||||
|
||||
args = exec_init(argv);
|
||||
args.redirect_stdout = args.redirect_stderr = EXEC_DEVNULL;
|
||||
res = do_exec(&args);
|
||||
if (res.pid < 0)
|
||||
eerrorx("%s: failed to run umount: %s", applet, strerror(errno));
|
||||
return res.pid;
|
||||
}
|
||||
|
||||
static void fuser_run(struct run_queue *rp, const char *fuser_opt)
|
||||
{
|
||||
static int fuser_exec_failed = 0;
|
||||
const char *argv[] = { "fuser", fuser_opt, rp->mntpath, NULL };
|
||||
struct exec_result res;
|
||||
struct exec_args args;
|
||||
|
||||
/* if exec failed, fuser likely doesn't exist. so don't retry */
|
||||
if (fuser_exec_failed)
|
||||
return;
|
||||
|
||||
args = exec_init(argv);
|
||||
args.redirect_stdout = EXEC_MKPIPE;
|
||||
args.redirect_stderr = EXEC_DEVNULL;
|
||||
res = do_exec(&args);
|
||||
if (res.pid < 0) {
|
||||
fuser_exec_failed = 1;
|
||||
} else {
|
||||
rp->fuser_pid = res.pid;
|
||||
rp->fuser_stdoutfd = res.proc_stdout;
|
||||
rp->fuser_exec_time = tm_now();
|
||||
}
|
||||
}
|
||||
|
||||
static int fuser_decide(struct run_queue *rp,
|
||||
const char *fuser_opt, const char *fuser_kill_prefix)
|
||||
{
|
||||
char buf[1<<12];
|
||||
char selfpid[64];
|
||||
int read_maybe_truncated;
|
||||
ssize_t n;
|
||||
|
||||
if (rp->fuser_stdoutfd < 0)
|
||||
return 0;
|
||||
|
||||
buf[0] = ' ';
|
||||
n = read(rp->fuser_stdoutfd, buf + 1, sizeof buf - 3);
|
||||
close(rp->fuser_stdoutfd);
|
||||
rp->fuser_stdoutfd = -1;
|
||||
read_maybe_truncated = (n == sizeof buf - 3);
|
||||
if (n < 0 || read_maybe_truncated)
|
||||
return 0;
|
||||
while (n > 0 && buf[n] == '\n')
|
||||
--n;
|
||||
buf[n+1] = ' ';
|
||||
buf[n+2] = '\0';
|
||||
snprintf(selfpid, sizeof selfpid, " %lld ", (long long)getpid());
|
||||
|
||||
if (strstr(buf, selfpid)) {
|
||||
/* lets not kill ourselves */
|
||||
eerror("Unmounting %s failed because we are using it", rp->mntpath);
|
||||
return -1;
|
||||
} else if (strcmp(buf, " ") == 0) {
|
||||
if (rp->try_count >= TRY_MAX) {
|
||||
eerror("Unmounting %s failed but fuser finds no one using it", rp->mntpath);
|
||||
return -1;
|
||||
}
|
||||
/* it's possible that whatever was using the mount stopped
|
||||
* using it now, so allow 1 more retry */
|
||||
rp->try_count = TRY_MAX;
|
||||
return 0;
|
||||
} else {
|
||||
char sig[32];
|
||||
const char *argv[] = {
|
||||
"fuser", sig, "-k", fuser_opt, rp->mntpath, NULL
|
||||
};
|
||||
struct exec_result res;
|
||||
struct exec_args args = exec_init(argv);
|
||||
args.redirect_stdout = args.redirect_stderr = EXEC_DEVNULL;
|
||||
snprintf(sig, sizeof sig, "%s%s", fuser_kill_prefix,
|
||||
rp->try_count == TRY_MAX ? "KILL" : "TERM");
|
||||
res = do_exec(&args);
|
||||
if (res.pid > 0)
|
||||
rc_waitpid(res.pid);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static regex_t *
|
||||
get_regex(const char *string)
|
||||
{
|
||||
@ -385,7 +561,23 @@ int main(int argc, char **argv)
|
||||
char *real_path = NULL;
|
||||
int opt;
|
||||
int result;
|
||||
char *this_path;
|
||||
char *this_path, *argv0;
|
||||
const char *tmps;
|
||||
const char *fuser_opt, *fuser_kill_prefix;
|
||||
size_t num_mounts = 0;
|
||||
size_t unmount_index;
|
||||
int doing_unmount = 0;
|
||||
pid_t pid;
|
||||
int status, flags;
|
||||
int64_t tmp, next_retry, now;
|
||||
int64_t rc_fuser_timeout = -1;
|
||||
const char *umount_args[UMOUNT_ARGS_MAX];
|
||||
int umount_args_num = 0;
|
||||
const char **mounts = NULL;
|
||||
struct run_queue running[RUN_MAX] = {0};
|
||||
struct run_queue *rp;
|
||||
size_t num_running = 0, num_waiting = 0;
|
||||
enum { STATE_RUN, STATE_REAP, STATE_RETRY, STATE_END } state;
|
||||
|
||||
#define DO_REG(_var) \
|
||||
if (_var) free(_var); \
|
||||
@ -393,11 +585,46 @@ int main(int argc, char **argv)
|
||||
#define REG_FREE(_var) \
|
||||
if (_var) { regfree(_var); free(_var); }
|
||||
|
||||
argv0 = argv[0];
|
||||
applet = basename_c(argv[0]);
|
||||
memset (&args, 0, sizeof(args));
|
||||
args.mount_type = mount_to;
|
||||
args.netdev = net_ignore;
|
||||
args.mounts = rc_stringlist_new();
|
||||
args.process = process_mount;
|
||||
|
||||
if (strcmp(applet, "do_unmount") == 0) {
|
||||
doing_unmount = 1;
|
||||
while (argv[1]) {
|
||||
/* shift over */
|
||||
tmps = argv[1];
|
||||
argv[1] = argv0;
|
||||
++argv;
|
||||
--argc;
|
||||
|
||||
if (strcmp(tmps, "--") == 0)
|
||||
break;
|
||||
if (umount_args_num >= (int)ARRAY_SIZE(umount_args))
|
||||
eerrorx("%s: Too many umount arguments", applet);
|
||||
umount_args[umount_args_num++] = tmps;
|
||||
}
|
||||
|
||||
tmps = rc_conf_value("rc_fuser_timeout");
|
||||
if (tmps && (rc_fuser_timeout = parse_duration(tmps)) < 0)
|
||||
ewarn("%s: Invalid rc_fuser_timeout value: `%s`. "
|
||||
"Defaulting to 20", applet, tmps);
|
||||
if (rc_fuser_timeout < 0)
|
||||
rc_fuser_timeout = 20 * 1000;
|
||||
|
||||
tmps = getenv("RC_UNAME");
|
||||
if (!tmps || strcmp(tmps, "Linux") == 0) {
|
||||
fuser_opt = "-m";
|
||||
fuser_kill_prefix = "-";
|
||||
} else {
|
||||
fuser_opt = "-cm";
|
||||
fuser_kill_prefix = "-s";
|
||||
}
|
||||
}
|
||||
|
||||
while ((opt = getopt_long(argc, argv, getoptstring,
|
||||
longopts, (int *) 0)) != -1)
|
||||
@ -459,18 +686,13 @@ int main(int argc, char **argv)
|
||||
free(real_path);
|
||||
real_path = NULL;
|
||||
}
|
||||
nodes = find_mounts(&args);
|
||||
nodes = find_mounts(&args, &num_mounts);
|
||||
rc_stringlist_free(args.mounts);
|
||||
|
||||
REG_FREE(args.fstype_regex);
|
||||
REG_FREE(args.skip_fstype_regex);
|
||||
REG_FREE(args.node_regex);
|
||||
REG_FREE(args.skip_node_regex);
|
||||
REG_FREE(args.options_regex);
|
||||
REG_FREE(args.skip_options_regex);
|
||||
|
||||
if (doing_unmount)
|
||||
mounts = xmalloc(num_mounts * sizeof(*mounts));
|
||||
num_mounts = 0;
|
||||
result = EXIT_FAILURE;
|
||||
|
||||
/* We should report the mounts in reverse order to ease unmounting */
|
||||
TAILQ_FOREACH_REVERSE(s, nodes, rc_stringlist, entries) {
|
||||
if (point_regex &&
|
||||
@ -479,12 +701,162 @@ int main(int argc, char **argv)
|
||||
if (skip_point_regex &&
|
||||
regexec(skip_point_regex, s->value, 0, NULL, 0) == 0)
|
||||
continue;
|
||||
if (!rc_yesno(getenv("EINFO_QUIET")))
|
||||
if (doing_unmount)
|
||||
mounts[num_mounts++] = unescape_octal(s->value);
|
||||
else if (!rc_yesno(getenv("EINFO_QUIET")))
|
||||
printf("%s\n", s->value);
|
||||
result = EXIT_SUCCESS;
|
||||
}
|
||||
rc_stringlist_free(nodes);
|
||||
if (!doing_unmount)
|
||||
goto exit;
|
||||
|
||||
/* STATE_RUN:
|
||||
* can unmount => stays in STATE_RUN
|
||||
* cannot unmount (for any of the reasons below) => STATE_REAP
|
||||
* (a) nothing left to unmount
|
||||
* (b) running queue is full
|
||||
* (c) conflicts with running queue
|
||||
*
|
||||
* STATE_REAP:
|
||||
* successful reap => STATE_RUN
|
||||
* couldn't reap with WNOHANG and there are retries pending => STATE_RETRY
|
||||
* nothing left to reap => STATE_RETRY
|
||||
*
|
||||
* STATE_RETRY:
|
||||
* successfully launched a retry => STATE_REAP
|
||||
* need to wait before retring -> sleep
|
||||
* sleep successful => STATE_RETRY
|
||||
* sleep interrupted via SIGCHILD (EINTR) => STATE_REAP
|
||||
* nothing left to retry, reap
|
||||
* and nothing to run either => STATE_END
|
||||
* otherwise => STATE_RUN
|
||||
*/
|
||||
result = EXIT_SUCCESS;
|
||||
state = STATE_RUN;
|
||||
while (state != STATE_END) switch (state) {
|
||||
case STATE_RUN:
|
||||
for (unmount_index = 0; unmount_index < num_mounts; ++unmount_index) {
|
||||
const char *candidate = mounts[unmount_index];
|
||||
int safe_to_unmount = 1;
|
||||
for (size_t k = 0; safe_to_unmount && k < num_running; ++k)
|
||||
safe_to_unmount = !is_prefix(candidate, running[k].mntpath);
|
||||
for (size_t k = 0; safe_to_unmount && k < num_mounts; ++k)
|
||||
safe_to_unmount = !is_prefix(candidate, mounts[k]);
|
||||
if (!safe_to_unmount)
|
||||
continue;
|
||||
if (!is_mounted(candidate)) {
|
||||
/* probably a shared mount and got unmounted, remove */
|
||||
mounts[unmount_index--] = mounts[--num_mounts];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (num_running == RUN_MAX || unmount_index >= num_mounts) {
|
||||
state = STATE_REAP;
|
||||
break;
|
||||
}
|
||||
rp = running + num_running++;
|
||||
rp->mntpath = mounts[unmount_index];
|
||||
rp->last_exec_time = tm_now();
|
||||
rp->try_count = 0;
|
||||
rp->pid = run_umount(rp->mntpath, umount_args, umount_args_num);
|
||||
rp->fuser_pid = -1;
|
||||
rp->fuser_stdoutfd = -1;
|
||||
rp->fuser_exec_time = -1;
|
||||
mounts[unmount_index] = mounts[--num_mounts];
|
||||
break;
|
||||
case STATE_REAP:
|
||||
flags = (num_waiting > 0) ? WNOHANG : 0;
|
||||
pid = waitpid(-1, &status, flags);
|
||||
rp = NULL;
|
||||
for (size_t i = 0; i < num_running; ++i) {
|
||||
rp = running + i;
|
||||
if (rp->fuser_pid == pid)
|
||||
rp->fuser_pid = -1;
|
||||
if (rp->pid == pid && pid > 0)
|
||||
break;
|
||||
rp = NULL;
|
||||
}
|
||||
if (rp) {
|
||||
if ((WIFEXITED(status) && WEXITSTATUS(status) == 0) ||
|
||||
!is_mounted(rp->mntpath)) {
|
||||
einfo("Unmounted %s", rp->mntpath);
|
||||
*rp = running[--num_running];
|
||||
state = STATE_RUN;
|
||||
} else if (rp->try_count >= TRY_MAX) {
|
||||
eerror("Failed to unmount %s", rp->mntpath);
|
||||
*rp = running[--num_running];
|
||||
result = EXIT_FAILURE;
|
||||
} else { /* put into waiting queue */
|
||||
rp->pid = -1;
|
||||
rp->try_count += 1;
|
||||
num_waiting += 1;
|
||||
fuser_run(rp, fuser_opt);
|
||||
}
|
||||
} else {
|
||||
state = STATE_RETRY;
|
||||
}
|
||||
break;
|
||||
case STATE_RETRY:
|
||||
rp = NULL;
|
||||
next_retry = INT64_MAX;
|
||||
for (size_t i = 0; i < num_running; ++i) {
|
||||
if (running[i].pid > 0)
|
||||
continue;
|
||||
if (running[i].fuser_pid > 0)
|
||||
tmp = running[i].fuser_exec_time + rc_fuser_timeout;
|
||||
else
|
||||
tmp = running[i].last_exec_time + TRY_DELAY_MS;
|
||||
if (tmp < next_retry) {
|
||||
rp = running + i;
|
||||
next_retry = tmp;
|
||||
}
|
||||
}
|
||||
if (!rp) {
|
||||
state = (num_mounts > 0) ? STATE_RUN : STATE_END;
|
||||
break;
|
||||
}
|
||||
now = tm_now();
|
||||
if (next_retry > now) {
|
||||
int64_t sleep_for = next_retry - now;
|
||||
/* a child may become available for reaping *before* we
|
||||
* enter sleep. cap the timeout to stay responsive. */
|
||||
if (sleep_for > 500)
|
||||
sleep_for = 500;
|
||||
if (tm_sleep(sleep_for, 0) != 0 && errno == EINTR)
|
||||
state = STATE_REAP;
|
||||
now = tm_now();
|
||||
}
|
||||
if (next_retry <= now) {
|
||||
if (rp->fuser_pid > 0) {
|
||||
kill(rp->fuser_pid, SIGKILL);
|
||||
waitpid(rp->fuser_pid, NULL, 0);
|
||||
rp->fuser_pid = -1;
|
||||
}
|
||||
if (fuser_decide(rp, fuser_opt, fuser_kill_prefix) < 0) { /* abort */
|
||||
*rp = running[--num_running];
|
||||
result = EXIT_FAILURE;
|
||||
} else { /* retry */
|
||||
rp->last_exec_time = tm_now();
|
||||
rp->pid = run_umount(rp->mntpath,
|
||||
umount_args, umount_args_num);
|
||||
}
|
||||
num_waiting -= 1;
|
||||
state = STATE_REAP;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
exit:
|
||||
free(mounts);
|
||||
rc_stringlist_free(nodes);
|
||||
REG_FREE(args.fstype_regex);
|
||||
REG_FREE(args.skip_fstype_regex);
|
||||
REG_FREE(args.node_regex);
|
||||
REG_FREE(args.skip_node_regex);
|
||||
REG_FREE(args.options_regex);
|
||||
REG_FREE(args.skip_options_regex);
|
||||
REG_FREE(point_regex);
|
||||
REG_FREE(skip_point_regex);
|
||||
|
||||
|
||||
20
tools/manymounts.sh
Executable file
20
tools/manymounts.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# can be used for testing do_unmount:
|
||||
# # ./tools/manymounts.sh
|
||||
# # do_unmount -- -p '^/tmp/manymounts.*'
|
||||
|
||||
set -- "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F"
|
||||
|
||||
mntdir="/tmp/manymounts"
|
||||
for a in "$@"; do
|
||||
mkdir -p "${mntdir}/${a}"
|
||||
mount -t tmpfs -o size=256K tmpfs "${mntdir}/${a}"
|
||||
for b in "$@"; do
|
||||
mkdir -p "${mntdir}/${a}/${b}"
|
||||
mount -t tmpfs -o size=256K tmpfs "${mntdir}/${a}/${b}"
|
||||
for c in "$@"; do
|
||||
mkdir -p "${mntdir}/${a}/${b}/${c}"
|
||||
mount -t tmpfs -o size=256K tmpfs "${mntdir}/${a}/${b}/${c}"
|
||||
done
|
||||
done
|
||||
done
|
||||
Loading…
x
Reference in New Issue
Block a user