Add a reset command
The 'reset' command is meant to factory reset the local Podman and Toolbox installations. Every now and then early adopters and testers of Toolbox have to do this when their local Podman state has gotten irrecoverably broken due to some Podman bug. It's useful to have a command that encapsulates all the steps to do a factory reset, as opposed to having to spell them out separately. It's easier to document, helps with user support, and can enable less opaque error messages that suggest a way forward when nothing is working. Since this command is meant to be used when the Podman installation is completely broken, it must avoid using any Podman commands at all costs. This is why it cannot use 'podman stop' to stop any running containers, nor can it use 'podman unshare' to delete ~/.local/share/containers when running rootless. Instead, it relies on the user rebooting the machine for the former, and uses newgidmap(1), newuidmap(1) and unshare(1) to reimplement 'podman unshare' for the latter. Note that when running as root, some care has been taken to avoid removing directories that might be owned by the operating system. eg., on Fedora /var/lib/containers/sigstore is owned by the containers-common RPM. https://github.com/containers/toolbox/pull/295
This commit is contained in:
parent
4481769182
commit
2a099e8049
|
@ -13,7 +13,7 @@ __toolbox() {
|
|||
local MIN_VERSION=29
|
||||
local RAWHIDE_VERSION=32
|
||||
|
||||
local commands="create enter help init-container list rm rmi run"
|
||||
local commands="create enter help init-container list reset rm rmi run"
|
||||
|
||||
declare -A options
|
||||
local options=([create]="--candidate-registry --container --image --release" \
|
||||
|
|
|
@ -11,6 +11,7 @@ manuals = [
|
|||
'toolbox-init-container.1',
|
||||
'toolbox-help.1',
|
||||
'toolbox-list.1',
|
||||
'toolbox-reset.1',
|
||||
'toolbox-rm.1',
|
||||
'toolbox-rmi.1',
|
||||
'toolbox-run.1',
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
% toolbox-reset(1)
|
||||
|
||||
## NAME
|
||||
toolbox\-reset - Remove all local podman (and toolbox) state
|
||||
|
||||
## SYNOPSIS
|
||||
**toolbox reset**
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Removes all existing podman (and toolbox) containers, images and configuration.
|
||||
This can be used to factory reset your local Podman and Toolbox installations
|
||||
when something has gone irrecoverably wrong with the `podman(1)` and
|
||||
`toolbox(1)` commands.
|
||||
|
||||
This command can only be used on the host, and not from within a toolbox
|
||||
container, and is only expected to be used right after a fresh boot before any
|
||||
other `podman(1)` or `toolbox(1)` commands have been invoked.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
### Reset a broken Podman and Toolbox installation
|
||||
|
||||
```
|
||||
$ toolbox reset
|
||||
```
|
|
@ -68,6 +68,10 @@ Initialize a running container.
|
|||
|
||||
List existing toolbox containers and images.
|
||||
|
||||
**toolbox-reset(1)**
|
||||
|
||||
Remove all local podman (and toolbox) state.
|
||||
|
||||
**toolbox-rm(1)**
|
||||
|
||||
Remove one or more toolbox containers.
|
||||
|
|
202
toolbox
202
toolbox
|
@ -690,6 +690,103 @@ pull_base_toolbox_image()
|
|||
)
|
||||
|
||||
|
||||
unshare_userns_rm()
|
||||
(
|
||||
path="$1"
|
||||
|
||||
if ! unshare_directory=$(mktemp --directory --tmpdir "toolbox-unshare-userns-rm-XXXXXXXXXX" 2>&3); then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: directory couldn't be created" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! touch "$unshare_directory/map" 2>&3; then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: file couldn't be created" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
exec 6>"$unshare_directory/map"
|
||||
if ! flock 6 2>&3; then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: lock couldn't be acquired" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$base_toolbox_command: parsing /etc/subgid" >&3
|
||||
|
||||
if ! subgid_entry=$(grep "^$USER:" /etc/subgid 2>&3); then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: no entry in /etc/subgid" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
userns_gid_start=$(echo "$subgid_entry" | cut --delimiter ":" --fields 2 2>&3)
|
||||
if ! is_integer "$userns_gid_start"; then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: cannot parse the first sub-GID" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
userns_gid_len=$(echo "$subgid_entry" | cut --delimiter ":" --fields 3 2>&3)
|
||||
if ! is_integer "$userns_gid_len"; then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: cannot parse the sub-GID count" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$base_toolbox_command: parsing /etc/subuid" >&3
|
||||
|
||||
if ! subuid_entry=$(grep "^$USER:" /etc/subuid 2>&3); then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: no entry in /etc/subuid" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
userns_uid_start=$(echo "$subuid_entry" | cut --delimiter ":" --fields 2 2>&3)
|
||||
if ! is_integer "$userns_uid_start"; then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: cannot parse the first sub-UID" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
userns_uid_len=$(echo "$subuid_entry" | cut --delimiter ":" --fields 3 2>&3)
|
||||
if ! is_integer "$userns_uid_len"; then
|
||||
echo "$base_toolbox_command: failed to enter user namespace: cannot parse the sub-UID count" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$base_toolbox_command: unsharing user namespace" >&3
|
||||
|
||||
unshare --user sh -c "flock $unshare_directory/map rm --force --recursive $path" 2>&3 &
|
||||
unshare_pid="$!"
|
||||
|
||||
echo "$base_toolbox_command: setting UID map of user namespace" >&3
|
||||
|
||||
if ! newgidmap "$unshare_pid" 0 "$user_id_real" 1 1 "$userns_gid_start" "$userns_gid_len" 2>&3; then
|
||||
echo "$base_toolbox_command: failed to set GID mapping of user namespace" >&2
|
||||
kill -9 "$unshare_pid" 2>&3
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! newuidmap "$unshare_pid" 0 "$user_id_real" 1 1 "$userns_uid_start" "$userns_uid_len" 2>&3; then
|
||||
echo "$base_toolbox_command: failed to set UID mapping of user namespace" >&2
|
||||
kill -9 "$unshare_pid" 2>&3
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$base_toolbox_command: UID map of user namespace:" >&3
|
||||
cat /proc/$unshare_pid/uid_map 1>&3 2>&3
|
||||
|
||||
if ! flock --unlock 6 2>&3; then
|
||||
echo "$base_toolbox_command: failed to remove $path: lock couldn't be unlocked" >&2
|
||||
kill -9 "$unshare_pid" 2>&3
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! wait "$unshare_pid" 2>&3; then
|
||||
echo "$base_toolbox_command: failed to remove $path" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
rm --force --recursive "$unshare_directory" 2>&3
|
||||
|
||||
return 0
|
||||
)
|
||||
|
||||
|
||||
create()
|
||||
(
|
||||
enter_command_skip="$1"
|
||||
|
@ -1599,6 +1696,87 @@ remove_images()
|
|||
)
|
||||
|
||||
|
||||
reset()
|
||||
(
|
||||
do_reset=false
|
||||
prompt_for_reset=true
|
||||
ret_val=0
|
||||
|
||||
if [ "$user_id_real" -eq 0 ] 2>&3; then
|
||||
if [ -d /run/containers ] 2>&3; then
|
||||
echo "$base_toolbox_command: The 'reset' command cannot be used after other commands" >&2
|
||||
echo "Reboot the system before using it again." >&2
|
||||
echo "Try '$base_toolbox_command --help' for more information." >&2
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if [ -d "$XDG_RUNTIME_DIR"/overlay-containers ] 2>&3 \
|
||||
|| [ -d "$XDG_RUNTIME_DIR"/overlay-layers ] 2>&3 \
|
||||
|| [ -d "$XDG_RUNTIME_DIR"/overlay-locks ] 2>&3; then
|
||||
echo "$base_toolbox_command: The 'reset' command cannot be used after other commands" >&2
|
||||
echo "Reboot the system before using it again." >&2
|
||||
echo "Try '$base_toolbox_command --help' for more information." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if $assume_yes; then
|
||||
do_reset=true
|
||||
prompt_for_reset=false
|
||||
fi
|
||||
|
||||
if $prompt_for_reset; then
|
||||
echo "All existing podman (and toolbox) containers and images will be removed."
|
||||
|
||||
prompt=$(printf "Continue? [y/N]:")
|
||||
if ask_for_confirmation "n" "$prompt"; then
|
||||
do_reset=true
|
||||
else
|
||||
do_reset=false
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! $do_reset; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$base_toolbox_command: resetting local state" >&3
|
||||
|
||||
if [ "$user_id_real" -eq 0 ] 2>&3; then
|
||||
if ! rm --force --recursive /var/lib/containers/cache >/dev/null 2>&3; then
|
||||
echo "$base_toolbox_command: failed to remove /var/lib/containers/cache" >&2
|
||||
ret_val=1
|
||||
fi
|
||||
|
||||
if ! rm --force --recursive /var/lib/containers/sigstore/* >/dev/null 2>&3; then
|
||||
echo "$base_toolbox_command: failed to remove the contents of /var/lib/containers/sigstore" >&2
|
||||
ret_val=1
|
||||
fi
|
||||
|
||||
if ! rm --force --recursive /var/lib/containers/storage >/dev/null 2>&3; then
|
||||
echo "$base_toolbox_command: failed to remove /var/lib/containers/storage" >&2
|
||||
ret_val=1
|
||||
fi
|
||||
else
|
||||
if ! unshare_userns_rm "$HOME/.local/share/containers"; then
|
||||
ret_val=1
|
||||
fi
|
||||
|
||||
if ! rm --force --recursive "$HOME/.config/containers" >/dev/null 2>&3; then
|
||||
echo "$base_toolbox_command: failed to remove $HOME/.config/containers" >&2
|
||||
ret_val=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! rm --force --recursive "$HOME/.config/toolbox" >/dev/null 2>&3; then
|
||||
echo "$base_toolbox_command: failed to remove $HOME/.config/toolbox" >&2
|
||||
ret_val=1
|
||||
fi
|
||||
|
||||
return "$ret_val"
|
||||
)
|
||||
|
||||
|
||||
exit_if_extra_operand()
|
||||
{
|
||||
if [ "$1" != "" ]; then
|
||||
|
@ -1910,6 +2088,11 @@ if [ -f /run/.containerenv ] 2>&3; then
|
|||
"$init_container_user"
|
||||
exit "$?"
|
||||
;;
|
||||
reset )
|
||||
echo "$base_toolbox_command: The 'reset' command cannot be used inside containers" >&2
|
||||
echo "Try '$base_toolbox_command --help' for more information." >&2
|
||||
exit 1
|
||||
;;
|
||||
* )
|
||||
echo "$base_toolbox_command: unrecognized command '$op'" >&2
|
||||
echo "Try '$base_toolbox_command --help' for more information." >&2
|
||||
|
@ -1918,9 +2101,11 @@ if [ -f /run/.containerenv ] 2>&3; then
|
|||
esac
|
||||
fi
|
||||
|
||||
if [ "$op" != "reset" ] 2>&3; then
|
||||
if ! migrate; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
case $op in
|
||||
create )
|
||||
|
@ -2082,6 +2267,23 @@ case $op in
|
|||
|
||||
exit
|
||||
;;
|
||||
reset )
|
||||
while has_prefix "$1" -; do
|
||||
case $1 in
|
||||
-h | --help )
|
||||
help "$op"
|
||||
exit
|
||||
;;
|
||||
* )
|
||||
exit_if_unrecognized_option "$1"
|
||||
esac
|
||||
shift
|
||||
done
|
||||
exit_if_extra_operand "$1"
|
||||
|
||||
reset
|
||||
exit "$?"
|
||||
;;
|
||||
rm | rmi )
|
||||
rm_all=false
|
||||
rm_force=false
|
||||
|
|
Loading…
Reference in New Issue