Compare commits

..

8 Commits

Author SHA1 Message Date
Yu Watanabe 4171837be6 bash-completion: move shell-completion for log-level or friends to systemctl 2019-12-21 19:23:02 +01:00
Zbigniew Jędrzejewski-Szmek 1234d0f63e
Merge pull request #14409 from poettering/shutdown-modernization
some smaller modernizations to the shutdown loop
2019-12-21 19:21:10 +01:00
Lennart Poettering 4ca8072fd6 umount: when we fail to detach a loopback device, set the auto-clear flag
We might get lucky and this cleans up things later on automatically for
us.
2019-12-20 18:37:24 +01:00
Lennart Poettering b877c3b06f umount: check LO_FLAGS_AUTOCLEAR after LOOP_CLR_FD claimed success
Fixes: #14410
Replaces: #14386
2019-12-20 18:16:05 +01:00
Lennart Poettering 63135a2d8d umount: detect root loopback device the same way as we detect root DM devices
get_block_device() is just the nicer way to do it (since it also odes
btrfs). Also, let's already collect the dev_t of the loopback device
when we enumerate things, that allows us to do the checks simpler
without constantly stat()ing things over and over again.
2019-12-20 18:16:02 +01:00
Lennart Poettering 88287615e6 umount: show correct error message
We fucked up errno vs. r two times, let's correct that.

While we are at it, let's handle the error first, like we usually do,
and the clean case without indentation.
2019-12-20 18:15:59 +01:00
Lennart Poettering 610f9a42c4 umount: remove unneeded variable 2019-12-20 18:15:56 +01:00
Lennart Poettering 49f80dcec8 umount: line break comments again
break them like we usually do, taking our intended line width into
account.
2019-12-20 18:15:47 +01:00
3 changed files with 98 additions and 87 deletions

View File

@ -211,6 +211,9 @@ _systemctl () {
[FILE]='link switch-root' [FILE]='link switch-root'
[TARGETS]='set-default' [TARGETS]='set-default'
[MACHINES]='list-machines' [MACHINES]='list-machines'
[LOG_LEVEL]='log-level'
[LOG_TARGET]='log-target'
[SERVICE_WATCHDOGS]='service-watchdogs'
) )
for ((i=0; i < COMP_CWORD; i++)); do for ((i=0; i < COMP_CWORD; i++)); do
@ -326,6 +329,12 @@ _systemctl () {
elif __contains_word "$verb" ${VERBS[TARGETS]}; then elif __contains_word "$verb" ${VERBS[TARGETS]}; then
comps=$( __systemctl $mode list-unit-files --type target --full --all "$cur*" \ comps=$( __systemctl $mode list-unit-files --type target --full --all "$cur*" \
| { while read -r a b; do echo " $a"; done; } ) | { while read -r a b; do echo " $a"; done; } )
elif __contains_word "$verb" ${VERBS[LOG_LEVEL]}; then
comps='debug info notice warning err crit alert emerg'
elif __contains_word "$verb" ${VERBS[LOG_TARGET]}; then
comps='console journal kmsg journal-or-kmsg null'
elif __contains_word "$verb" ${VERBS[SERVICE_WATCHDOGS]}; then
comps='on off'
fi fi
COMPREPLY=( $(compgen -o filenames -W '$comps' -- "$cur_orig") ) COMPREPLY=( $(compgen -o filenames -W '$comps' -- "$cur_orig") )

View File

@ -58,11 +58,8 @@ _systemd_analyze() {
[STANDALONE]='time blame plot dump unit-paths exit-status condition calendar timestamp timespan' [STANDALONE]='time blame plot dump unit-paths exit-status condition calendar timestamp timespan'
[CRITICAL_CHAIN]='critical-chain' [CRITICAL_CHAIN]='critical-chain'
[DOT]='dot' [DOT]='dot'
[LOG_LEVEL]='log-level'
[LOG_TARGET]='log-target'
[VERIFY]='verify' [VERIFY]='verify'
[SECCOMP_FILTER]='syscall-filter' [SECCOMP_FILTER]='syscall-filter'
[SERVICE_WATCHDOGS]='service-watchdogs'
[CAT_CONFIG]='cat-config' [CAT_CONFIG]='cat-config'
[SECURITY]='security' [SECURITY]='security'
) )
@ -119,20 +116,6 @@ _systemd_analyze() {
comps='--help --version --system --user --global --from-pattern --to-pattern --order --require' comps='--help --version --system --user --global --from-pattern --to-pattern --order --require'
fi fi
elif __contains_word "$verb" ${VERBS[LOG_LEVEL]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user'
else
comps='debug info notice warning err crit alert emerg'
fi
elif __contains_word "$verb" ${VERBS[LOG_TARGET]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user'
else
comps='console journal kmsg journal-or-kmsg null'
fi
elif __contains_word "$verb" ${VERBS[SECCOMP_FILTER]}; then elif __contains_word "$verb" ${VERBS[SECCOMP_FILTER]}; then
if [[ $cur = -* ]]; then if [[ $cur = -* ]]; then
comps='--help --version --no-pager' comps='--help --version --no-pager'
@ -148,13 +131,6 @@ _systemd_analyze() {
compopt -o filenames compopt -o filenames
fi fi
elif __contains_word "$verb" ${VERBS[SERVICE_WATCHDOGS]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user'
else
comps='on off'
fi
elif __contains_word "$verb" ${VERBS[CAT_CONFIG]}; then elif __contains_word "$verb" ${VERBS[CAT_CONFIG]}; then
if [[ $cur = -* ]]; then if [[ $cur = -* ]]; then
comps='--help --version --root --no-pager' comps='--help --version --root --no-pager'

View File

@ -249,8 +249,10 @@ static int loopback_list_get(MountPoint **head) {
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;
const char *dn; const char *dn;
MountPoint *lb; MountPoint *lb;
dev_t devnum;
if (sd_device_get_devname(d, &dn) < 0) if (sd_device_get_devnum(d, &devnum) < 0 ||
sd_device_get_devname(d, &dn) < 0)
continue; continue;
p = strdup(dn); p = strdup(dn);
@ -263,6 +265,7 @@ static int loopback_list_get(MountPoint **head) {
*lb = (MountPoint) { *lb = (MountPoint) {
.path = TAKE_PTR(p), .path = TAKE_PTR(p),
.devnum = devnum,
}; };
LIST_PREPEND(mount_point, *head, lb); LIST_PREPEND(mount_point, *head, lb);
@ -325,7 +328,7 @@ static int dm_list_get(MountPoint **head) {
static int delete_loopback(const char *device) { static int delete_loopback(const char *device) {
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
int r; struct loop_info64 info;
assert(device); assert(device);
@ -333,15 +336,54 @@ static int delete_loopback(const char *device) {
if (fd < 0) if (fd < 0)
return errno == ENOENT ? 0 : -errno; return errno == ENOENT ? 0 : -errno;
r = ioctl(fd, LOOP_CLR_FD, 0); if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
if (r >= 0) if (errno == ENXIO) /* Nothing bound, didn't do anything */
return 0;
if (errno != EBUSY)
return log_debug_errno(errno, "Failed to clear loopback device %s: %m", device);
if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
if (errno == ENXIO) /* What? Suddenly detached after all? That's fine by us then. */
return 1;
log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device);
return -EBUSY; /* propagate original error */
}
if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR)) /* someone else already set LO_FLAGS_AUTOCLEAR for us? fine by us */
return -EBUSY; /* propagate original error */
info.lo_flags |= LO_FLAGS_AUTOCLEAR;
if (ioctl(fd, LOOP_SET_STATUS64, &info) < 0) {
if (errno == ENXIO) /* Suddenly detached after all? Fine by us */
return 1;
log_debug_errno(errno, "Failed to set LO_FLAGS_AUTOCLEAR flag for loop device %s, ignoring: %m", device);
} else
log_debug("Successfully set LO_FLAGS_AUTOCLEAR flag for loop device %s.", device);
return -EBUSY;
}
if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
/* If the LOOP_CLR_FD above succeeded we'll see ENXIO here. */
if (errno == ENXIO)
log_debug("Successfully detached loopback device %s.", device);
else
log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device); /* the LOOP_CLR_FD at least worked, let's hope for the best */
return 1; return 1;
}
/* ENXIO: not bound, so no error */ /* Linux makes LOOP_CLR_FD succeed whenever LO_FLAGS_AUTOCLEAR is set without actually doing
if (errno == ENXIO) * anything. Very confusing. Let's hence not claim we did anything in this case. */
return 0; if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR))
log_debug("Successfully called LOOP_CLR_FD on a loopback device %s with autoclear set, which is a NOP.", device);
else
log_debug("Weird, LOOP_CLR_FD succeeded but the device is still attached on %s.", device);
return -errno; return -EBUSY; /* Nothing changed, the device is still attached, hence it apparently is still busy */
} }
static int delete_dm(dev_t devnum) { static int delete_dm(dev_t devnum) {
@ -460,8 +502,8 @@ static int umount_with_timeout(MountPoint *m, int umount_log_level) {
return r; return r;
} }
/* This includes remounting readonly, which changes the kernel mount options. /* This includes remounting readonly, which changes the kernel mount options. Therefore the list passed to
* Therefore the list passed to this function is invalidated, and should not be reused. */ * this function is invalidated, and should not be reused. */
static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) { static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) {
MountPoint *m; MountPoint *m;
int n_failed = 0; int n_failed = 0;
@ -471,26 +513,18 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, int umount
LIST_FOREACH(mount_point, m, *head) { LIST_FOREACH(mount_point, m, *head) {
if (m->try_remount_ro) { if (m->try_remount_ro) {
/* We always try to remount directories /* We always try to remount directories read-only first, before we go on and umount
* read-only first, before we go on and umount
* them. * them.
* *
* Mount points can be stacked. If a mount * Mount points can be stacked. If a mount point is stacked below / or /usr, we
* point is stacked below / or /usr, we * cannot umount or remount it directly, since there is no way to refer to the
* cannot umount or remount it directly, * underlying mount. There's nothing we can do about it for the general case, but we
* since there is no way to refer to the * can do something about it if it is aliased somewhere else via a bind mount. If we
* underlying mount. There's nothing we can do * explicitly remount the super block of that alias read-only we hence should be
* about it for the general case, but we can * relatively safe regarding keeping a dirty fs we cannot otherwise see.
* do something about it if it is aliased
* somewhere else via a bind mount. If we
* explicitly remount the super block of that
* alias read-only we hence should be
* relatively safe regarding keeping a dirty fs
* we cannot otherwise see.
* *
* Since the remount can hang in the instance of * Since the remount can hang in the instance of remote filesystems, we remount
* remote filesystems, we remount asynchronously * asynchronously and skip the subsequent umount if it fails. */
* and skip the subsequent umount if it fails. */
if (remount_with_timeout(m, umount_log_level) < 0) { if (remount_with_timeout(m, umount_log_level) < 0) {
/* Remount failed, but try unmounting anyway, /* Remount failed, but try unmounting anyway,
* unless this is a mount point we want to skip. */ * unless this is a mount point we want to skip. */
@ -501,9 +535,8 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, int umount
} }
} }
/* Skip / and /usr since we cannot unmount that /* Skip / and /usr since we cannot unmount that anyway, since we are running from it. They
* anyway, since we are running from it. They have * have already been remounted ro. */
* already been remounted ro. */
if (nonunmountable_path(m->path)) if (nonunmountable_path(m->path))
continue; continue;
@ -526,13 +559,14 @@ static int swap_points_list_off(MountPoint **head, bool *changed) {
LIST_FOREACH_SAFE(mount_point, m, n, *head) { LIST_FOREACH_SAFE(mount_point, m, n, *head) {
log_info("Deactivating swap %s.", m->path); log_info("Deactivating swap %s.", m->path);
if (swapoff(m->path) == 0) { if (swapoff(m->path) < 0) {
*changed = true;
mount_point_free(head, m);
} else {
log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path);
n_failed++; n_failed++;
continue;
} }
*changed = true;
mount_point_free(head, m);
} }
return n_failed; return n_failed;
@ -540,37 +574,31 @@ static int swap_points_list_off(MountPoint **head, bool *changed) {
static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
MountPoint *m, *n; MountPoint *m, *n;
int n_failed = 0, k; int n_failed = 0, r;
struct stat root_st; dev_t rootdev = 0;
assert(head); assert(head);
assert(changed); assert(changed);
k = lstat("/", &root_st); (void) get_block_device("/", &rootdev);
LIST_FOREACH_SAFE(mount_point, m, n, *head) { LIST_FOREACH_SAFE(mount_point, m, n, *head) {
int r; if (major(rootdev) != 0 && rootdev == m->devnum) {
struct stat loopback_st;
if (k >= 0 &&
major(root_st.st_dev) != 0 &&
lstat(m->path, &loopback_st) >= 0 &&
root_st.st_dev == loopback_st.st_rdev) {
n_failed++; n_failed++;
continue; continue;
} }
log_info("Detaching loopback %s.", m->path); log_info("Detaching loopback %s.", m->path);
r = delete_loopback(m->path); r = delete_loopback(m->path);
if (r >= 0) { if (r < 0) {
if (r > 0) log_full_errno(umount_log_level, r, "Could not detach loopback %s: %m", m->path);
*changed = true;
mount_point_free(head, m);
} else {
log_full_errno(umount_log_level, errno, "Could not detach loopback %s: %m", m->path);
n_failed++; n_failed++;
continue;
} }
if (r > 0)
*changed = true;
mount_point_free(head, m);
} }
return n_failed; return n_failed;
@ -579,39 +607,37 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed, int umo
static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
MountPoint *m, *n; MountPoint *m, *n;
int n_failed = 0, r; int n_failed = 0, r;
dev_t rootdev; dev_t rootdev = 0;
assert(head); assert(head);
assert(changed); assert(changed);
r = get_block_device("/", &rootdev); (void) get_block_device("/", &rootdev);
if (r <= 0)
rootdev = 0;
LIST_FOREACH_SAFE(mount_point, m, n, *head) { LIST_FOREACH_SAFE(mount_point, m, n, *head) {
if (major(rootdev) != 0 && rootdev == m->devnum) { if (major(rootdev) != 0 && rootdev == m->devnum) {
n_failed ++; n_failed ++;
continue; continue;
} }
log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum)); log_info("Detaching DM %s (%u:%u).", m->path, major(m->devnum), minor(m->devnum));
r = delete_dm(m->devnum); r = delete_dm(m->devnum);
if (r >= 0) { if (r < 0) {
*changed = true; log_full_errno(umount_log_level, r, "Could not detach DM %s: %m", m->path);
mount_point_free(head, m);
} else {
log_full_errno(umount_log_level, errno, "Could not detach DM %s: %m", m->path);
n_failed++; n_failed++;
continue;
} }
*changed = true;
mount_point_free(head, m);
} }
return n_failed; return n_failed;
} }
static int umount_all_once(bool *changed, int umount_log_level) { static int umount_all_once(bool *changed, int umount_log_level) {
int r;
_cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
int r;
assert(changed); assert(changed);