toolbox/toolbox
Debarshi Ray 3afb2bc4c7 Suggest a way forward if no command has been specified
Instead of overwhelming the user with the entire reference
documentation, highlight some of the more common commands that a new
user is likely to be interested in. This is concise enough to not annoy
seasoned users who might have just committed a typo.

This should smoothen the onboarding experience by making the commands
self-documenting.

https://github.com/debarshiray/toolbox/issues/59
2019-04-11 16:26:44 +02:00

1411 lines
44 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
#
# Copyright © 2018 2019 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
exec 3>/dev/null
arguments=""
base_toolbox_command=$(basename "$0" 2>&3)
base_toolbox_image=""
environment_variables="COLORTERM \
DBUS_SESSION_BUS_ADDRESS \
DESKTOP_SESSION \
DISPLAY \
LANG \
SHELL \
SSH_AUTH_SOCK \
TERM \
VTE_VERSION \
XDG_CURRENT_DESKTOP \
XDG_DATA_DIRS \
XDG_MENU_PREFIX \
XDG_RUNTIME_DIR \
XDG_SEAT \
XDG_SESSION_DESKTOP \
XDG_SESSION_ID \
XDG_SESSION_TYPE \
XDG_VTNR"
fgc=""
podman_pid=""
prefix_sudo=""
registry="registry.fedoraproject.org"
registry_candidate="candidate-registry.fedoraproject.org"
release=""
release_default=""
spinner_animation="[>----] [=>---] [==>--] [===>-] [====>] [----<] [---<=] [--<==] [-<===] [<====]"
spinner_template="toolbox-spinner-XXXXXXXXXX"
tab="$(printf '\t')"
toolbox_container=""
toolbox_container_default=""
toolbox_container_prefix_default=""
toolbox_image=""
toolbox_prompt="🔹[\u@\h \W]\\$ "
user_id_real=$(id -ru 2>&3)
verbose=false
LGC='\033[1;32m' # Light Green Color
LBC='\033[1;34m' # Light Blue Color
NC='\033[0m' # No Color
has_prefix()
(
str="$1"
prefix="$2"
ret_val=1
case "$str" in
"$prefix"* )
ret_val=0
;;
* )
ret_val=1
;;
esac
return $ret_val
)
has_substring()
(
haystack="$1"
needle="$2"
ret_val=1
case "$haystack" in
*"$needle"* )
ret_val=0
;;
* )
ret_val=1
;;
esac
return $ret_val
)
is_integer()
{
[ "$1" != "" ] && [ "$1" -eq "$1" ] 2>&3
return $?
}
save_positional_parameters()
{
for i; do
printf "%s\\n" "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" 2>&3
done
echo " "
}
spinner_start()
(
directory="$1"
message="$2"
if $verbose; then
rm --force --recursive "$directory" 2>&3
return 0
fi
if ! touch "$directory/spinner-start" 2>&3; then
echo "$base_toolbox_command: unable to start spinner: spinner start file couldn't be created" >&2
return 1
fi
printf "%s" "$message"
tput civis 2>&3
exec 4>"$directory/spinner-start"
if ! flock 4 2>&3; then
echo "$base_toolbox_command: unable to start spinner: spinner lock couldn't be acquired" >&2
return 1
fi
(
while [ -f "$directory/spinner-start" ]; do
echo "$spinner_animation" | sed "s/ /\n/g" 2>&3 | while read -r frame; do
if ! [ -f "$directory/spinner-start" ] 2>&3; then
break
fi
printf "%s" "$frame"
frame_len=${#frame}
i=0
while [ "$i" -lt "$frame_len" ]; do
printf "\b"
i=$((i + 1))
done
sleep 1
done
done
printf "\033[2K" # delete entire line regardless of cursor position
printf "\r"
tput cnorm 2>&3
) &
return 0
)
spinner_stop()
(
$verbose && return
directory="$1"
exec 4>"$directory/spinner-start"
if ! rm "$directory/spinner-start" 2>&3; then
echo "$base_toolbox_command: unable to stop spinner: spinner start file couldn't be removed" >&2
return
fi
if ! flock 4 2>&3; then
echo "$base_toolbox_command: unable to stop spinner: spinner lock couldn't be acquired" >&2
return
fi
rm --force --recursive "$directory" 2>&3
)
check_image_for_volumes()
(
image="$1"
echo "$base_toolbox_command: checking if image $image has volumes for host bind mounts" >&3
if volumes=$($prefix_sudo podman inspect \
--format "{{.Config.Volumes}}" \
--type image \
"$image" 2>&3); then
if echo "$volumes" | grep "/dev/dri\|/dev/fuse" >/dev/null 2>&3; then
echo "$base_toolbox_command: image $image has volumes for host bind mounts:" >&3
echo "$base_toolbox_command: $volumes" >&3
echo "$base_toolbox_command: consider re-creating image $image" >&3
fi
fi
)
configure_working_container()
(
working_container_name="$1"
kcm_ccache_configuration="$2"
if [ "$(readlink /home)" = var/home ] ; then
need_home_link=true
else
need_home_link=false
fi
if $need_home_link ; then
if ! $prefix_sudo buildah run "$working_container_name" -- \
sh -c 'rmdir /home && mkdir -m 0755 -p /var/home && ln -s var/home /home' 2>&3; then
$prefix_sudo buildah rm "$working_container_name" >/dev/null 2>&3
echo "$base_toolbox_command: failed to make /home a symlink" >&2
return 1
fi
fi
if [ "$kcm_ccache_configuration" != "" ] 2>&3; then
if ! $prefix_sudo buildah copy "$working_container_name" \
"$kcm_ccache_configuration" \
/etc/krb5.conf.d >/dev/null 2>&3; then
$prefix_sudo buildah rm "$working_container_name" >/dev/null 2>&3
echo "$base_toolbox_command: failed to set KCM as the default Kerberos credential cache" >&2
return 1
fi
fi
if ! $prefix_sudo buildah run "$working_container_name" -- useradd \
--home-dir "$HOME" \
--no-create-home \
--shell "$SHELL" \
--uid "$user_id_real" \
--groups wheel \
"$USER" \
>/dev/null 2>&3; then
echo "$base_toolbox_command: failed to create user $USER with UID $user_id_real" >&2
return 1
fi
if ! $prefix_sudo buildah run "$working_container_name" -- passwd -d "$USER" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove password for user $USER" >&2
return 1
fi
if ! $prefix_sudo buildah run "$working_container_name" -- passwd -d root >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove password for user root" >&2
return 1
fi
if ! $prefix_sudo buildah config \
--label "com.github.debarshiray.toolbox=true" \
"$working_container_name" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to add com.github.debarshiray.toolbox=true" >&2
return 1
fi
if ! $prefix_sudo buildah config --user "$USER" "$working_container_name" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to configure the default user as $USER" >&2
return 1
fi
if ! $prefix_sudo buildah config --workingdir "$HOME" "$working_container_name" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to configure the initial working directory to $HOME" >&2
return 1
fi
return 0
)
create_environment_options()
{
echo "$environment_variables" \
| sed "s/ \+/\n/g" 2>&3 \
| {
environment_options=""
echo "$base_toolbox_command: creating list of environment variables to forward" >&3
value=""
while read -r variable; do
eval value="$""$variable"
echo "$base_toolbox_command: $variable=$value" >&3
environment_options="$environment_options --env=$variable=$value"
done
environment_options=${environment_options#" "}
echo "$base_toolbox_command: created options for environment variables to forward" >&3
echo "$environment_options" >&3
echo "$environment_options"
}
}
create_toolbox_image_name()
(
# Based on the ResolveName function implemented in:
# https://github.com/containers/buildah/blob/master/util/util.go
if image_reference_can_be_id "$base_toolbox_image"; then
if base_toolbox_image_id=$($prefix_sudo podman inspect \
--format "{{.Id}}" \
--type image \
"$base_toolbox_image" 2>&3); then
if has_prefix "$base_toolbox_image_id" "$base_toolbox_image"; then
echo "$base_toolbox_image-$USER:latest"
return 0
fi
fi
fi
basename=$(image_reference_get_basename "$base_toolbox_image")
if [ "$basename" = "" ] 2>&3; then
return 100
fi
tag=$(image_reference_get_tag "$base_toolbox_image")
if [ "$tag" = "" ] 2>&3; then
echo "$basename-$USER"
else
echo "$basename-$USER:$tag"
fi
return 0
)
get_host_version_id()
(
# shellcheck disable=SC1091
. /etc/os-release
echo "$VERSION_ID"
)
image_reference_can_be_id()
(
image="$1"
echo "$image" | grep "^[a-f0-9]\{6,64\}$" >/dev/null 2>&3
return $?
)
image_reference_get_basename()
(
image="$1"
domain=$(image_reference_get_domain "$image")
remainder=${image#$domain}
path=${remainder%:*}
basename=${path##*/}
echo "$basename"
)
image_reference_get_domain()
(
image="$1"
image_reference_has_domain "$image" && domain=${image%%/*}
echo "$domain"
)
image_reference_get_tag()
(
image="$1"
domain=$(image_reference_get_domain "$image")
remainder=${image#$domain}
tag=""
if (echo "$remainder" | grep ":" >/dev/null 2>&3); then
tag=${remainder#*:}
fi
echo "$tag"
)
image_reference_has_domain()
(
# Based on the splitDockerDomain function implemented in:
# https://github.com/docker/distribution/blob/master/reference/normalize.go
image="$1"
if ! (echo "$image" | grep "/" >/dev/null 2>&3); then
return 1
fi
prefix=${image%%/*}
if ! (echo "$prefix" | grep "[.:]" >/dev/null 2>&3) && [ "$prefix" != "localhost" ] 2>&3; then
return 1
fi
return 0
)
images_get_details()
(
images="$1"
if ! echo "$images" | while read -r image; do
[ "$image" = "" ] 2>&3 && continue
if ! $prefix_sudo podman images \
--format "{{.ID}} {{.Repository}}:{{.Tag}} {{.Created}}" \
--noheading \
"$image" 2>&3; then
echo "$base_toolbox_command: failed to get details for image $image" >&2
return 1
fi
done; then
return 1
fi
return 0
)
pull_base_toolbox_image()
(
has_domain=false
if image_reference_can_be_id "$base_toolbox_image"; then
echo "$base_toolbox_command: looking for image $base_toolbox_image" >&3
if $prefix_sudo podman inspect --type image "$base_toolbox_image" >/dev/null 2>&3; then
return 0
fi
fi
image_reference_has_domain "$base_toolbox_image" && has_domain=true
if ! $has_domain; then
echo "$base_toolbox_command: looking for image localhost/$base_toolbox_image" >&3
if $prefix_sudo buildah pull localhost/$base_toolbox_image >/dev/null 2>&3; then
return 0
fi
fi
if $has_domain; then
base_toolbox_image_full="$base_toolbox_image"
else
base_toolbox_image_full="$registry/$fgc/$base_toolbox_image"
fi
echo "$base_toolbox_command: looking for image $base_toolbox_image_full" >&3
if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then
spinner_message="$base_toolbox_command: pulling $base_toolbox_image_full: "
if ! spinner_start "$spinner_directory" "$spinner_message"; then
spinner_directory=""
fi
else
echo "$base_toolbox_command: unable to start spinner: spinner directory not created" >&2
spinner_directory=""
fi
$prefix_sudo buildah pull $base_toolbox_image_full >/dev/null 2>&3
ret_val=$?
if [ "$spinner_directory" != "" ]; then
spinner_stop "$spinner_directory"
fi
return $ret_val
)
create()
(
dbus_system_bus_address="unix:path=/var/run/dbus/system_bus_socket"
kcm_ccache_configuration=""
kcm_socket=""
kcm_socket_bind=""
tmpfs_size=$((64 * 1024 * 1024)) # 64 MiB
working_container_name="toolbox-working-container-$(uuidgen --time)"
# shellcheck disable=SC2153
if [ "$DBUS_SYSTEM_BUS_ADDRESS" != "" ]; then
dbus_system_bus_address=$DBUS_SYSTEM_BUS_ADDRESS
fi
dbus_system_bus_path=$(echo "$dbus_system_bus_address" | cut --delimiter = --fields 2 2>&3)
dbus_system_bus_path=$(readlink --canonicalize "$dbus_system_bus_path" 2>&3)
# Note that 'systemctl show ...' doesn't terminate with a non-zero exit
# code when used with an unknown unit. eg.:
# $ systemctl show --value --property Listen foo
# $ echo $?
# 0
if ! kcm_socket_listen=$(systemctl show --value --property Listen sssd-kcm.socket 2>&3); then
echo "$base_toolbox_command: failed to use 'systemctl show'" >&3
kcm_socket_listen=""
elif [ "$kcm_socket_listen" = "" ] 2>&3; then
echo "$base_toolbox_command: failed to read property Listen from sssd-kcm.socket" >&3
else
echo "$base_toolbox_command: checking value $kcm_socket_listen of property Listen in sssd-kcm.socket" >&3
if ! (echo "$kcm_socket_listen" | grep " (Stream)$" >/dev/null 2>&3); then
echo "$base_toolbox_command: unknown socket in sssd-kcm.socket" >&2
echo "$base_toolbox_command: expected SOCK_STREAM" >&2
kcm_socket_listen=""
elif ! (echo "$kcm_socket_listen" | grep "^/" >/dev/null 2>&3); then
echo "$base_toolbox_command: unknown socket in sssd-kcm.socket" >&2
echo "$base_toolbox_command: expected file system socket in the AF_UNIX family" >&2
kcm_socket_listen=""
fi
fi
echo "$base_toolbox_command: parsing value $kcm_socket_listen of property Listen in sssd-kcm.socket" >&3
if [ "$kcm_socket_listen" != "" ] 2>&3; then
kcm_ccache_configuration="/etc/krb5.conf.d/kcm_default_ccache"
kcm_socket=${kcm_socket_listen%" (Stream)"}
kcm_socket_bind="--volume $kcm_socket:$kcm_socket"
fi
echo "$base_toolbox_command: checking if image $toolbox_image already exists" >&3
if ! $prefix_sudo podman inspect --type image $toolbox_image >/dev/null 2>&3; then
if ! pull_base_toolbox_image; then
echo "$base_toolbox_command: failed to pull base image $base_toolbox_image" >&2
exit 1
fi
if image_reference_has_domain "$base_toolbox_image"; then
base_toolbox_image_full="$base_toolbox_image"
else
if ! base_toolbox_image_full=$($prefix_sudo podman inspect \
--format "{{index .RepoTags 0}}" \
--type image \
"$base_toolbox_image" 2>&3); then
echo "$base_toolbox_command: failed to get RepoTag for base image $base_toolbox_image" >&2
exit 1
fi
echo "$base_toolbox_command: base image $base_toolbox_image resolved to $base_toolbox_image_full" >&3
fi
echo "$base_toolbox_command: trying to create working container $working_container_name" >&3
if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then
spinner_message="$base_toolbox_command: creating working container: "
if ! spinner_start "$spinner_directory" "$spinner_message"; then
spinner_directory=""
fi
else
echo "$base_toolbox_command: unable to start spinner: spinner directory not created" >&2
spinner_directory=""
fi
$prefix_sudo buildah from --name "$working_container_name" "$base_toolbox_image_full" >/dev/null 2>&3
ret_val=$?
if [ "$spinner_directory" != "" ]; then
spinner_stop "$spinner_directory"
fi
if [ $ret_val -ne 0 ]; then
echo "$base_toolbox_command: failed to create working container" >&2
exit 1
fi
echo "$base_toolbox_command: trying to configure working container $working_container_name" >&3
if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then
spinner_message="$base_toolbox_command: configuring working container: "
if ! spinner_start "$spinner_directory" "$spinner_message"; then
spinner_directory=""
fi
else
echo "$base_toolbox_command: unable to start spinner: spinner directory not created" >&2
spinner_directory=""
fi
configure_working_container "$working_container_name" "$kcm_ccache_configuration"
ret_val=$?
if [ "$spinner_directory" != "" ]; then
spinner_stop "$spinner_directory"
fi
if [ $ret_val -ne 0 ]; then
$prefix_sudo buildah rm "$working_container_name" >/dev/null 2>&3
exit 1
fi
echo "$base_toolbox_command: trying to create image $toolbox_image" >&3
if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then
spinner_message="$base_toolbox_command: creating image $toolbox_image: "
if ! spinner_start "$spinner_directory" "$spinner_message"; then
spinner_directory=""
fi
else
echo "$base_toolbox_command: unable to start spinner: spinner directory not created" >&2
spinner_directory=""
fi
$prefix_sudo buildah commit --rm "$working_container_name" "$toolbox_image" >/dev/null 2>&3
ret_val=$?
if [ "$spinner_directory" != "" ]; then
spinner_stop "$spinner_directory"
fi
if [ $ret_val -ne 0 ]; then
$prefix_sudo buildah rm "$working_container_name" >/dev/null 2>&3
echo "$base_toolbox_command: failed to create image $toolbox_image" >&2
exit 1
fi
echo "$base_toolbox_command: created image $toolbox_image" >&3
fi
check_image_for_volumes "$toolbox_image"
echo "$base_toolbox_command: checking if container $toolbox_container already exists" >&3
if $prefix_sudo podman inspect --type container $toolbox_container >/dev/null 2>&3; then
echo "$base_toolbox_command: container $toolbox_container already exists" >&2
exit 1
fi
total_ram=$(awk '( $1 == "MemTotal:" ) { print $2 }' /proc/meminfo 2>&3) # kibibytes
if is_integer "$total_ram"; then
tmpfs_size=$((total_ram * 1024 / 2)) # bytes
fi
max_uid_count=65536
max_minus_uid=$((max_uid_count - user_id_real))
uid_plus_one=$((user_id_real + 1))
echo "$base_toolbox_command: trying to create container $toolbox_container" >&3
if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then
spinner_message="$base_toolbox_command: creating container $toolbox_container: "
if ! spinner_start "$spinner_directory" "$spinner_message"; then
spinner_directory=""
fi
else
echo "$base_toolbox_command: unable to start spinner: spinner directory not created" >&2
spinner_directory=""
fi
# shellcheck disable=SC2086
$prefix_sudo podman create \
--group-add wheel \
--hostname toolbox \
--name $toolbox_container \
--network host \
--pid host \
--privileged \
--security-opt label=disable \
--tmpfs /dev/shm:size=$tmpfs_size \
--uidmap "$user_id_real":0:1 \
--uidmap 0:1:"$user_id_real" \
--uidmap "$uid_plus_one":"$uid_plus_one":"$max_minus_uid" \
$kcm_socket_bind \
--volume "$HOME":"$HOME":rslave \
--volume "$XDG_RUNTIME_DIR":"$XDG_RUNTIME_DIR" \
--volume "$dbus_system_bus_path":"$dbus_system_bus_path" \
--volume /dev/bus:/dev/bus \
--volume /dev/dri:/dev/dri \
--volume /dev/fuse:/dev/fuse \
--volume /media:/media:rslave \
--volume /mnt:/mnt:rslave \
--volume /run/media:/run/media:rslave \
$toolbox_image \
sleep +Inf >/dev/null 2>&3
ret_val=$?
if [ "$spinner_directory" != "" ]; then
spinner_stop "$spinner_directory"
fi
if [ $ret_val -ne 0 ]; then
echo "$base_toolbox_command: failed to create container $toolbox_container" >&2
exit 1
fi
echo "Created toolbox container: $toolbox_container"
enter_command=""
if [ "$toolbox_container" = "$toolbox_container_default" ] 2>&3; then
enter_command="$base_toolbox_command enter"
elif [ "$toolbox_container" = "$toolbox_container_prefix_default:$release" ] 2>&3; then
enter_command="$base_toolbox_command enter --release $release"
else
enter_command="$base_toolbox_command enter --container $toolbox_container"
fi
echo "Enter with: $enter_command"
)
enter()
(
shell_to_exec=/bin/bash
echo "$base_toolbox_command: checking if container $toolbox_container exists" >&3
if ! toolbox_image=$($prefix_sudo podman inspect \
--format "{{.ImageName}}" \
--type container \
$toolbox_container 2>&3); then
echo "$base_toolbox_command: container $toolbox_container not found" >&2
echo "Use the 'create' command to create a toolbox." >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
fi
echo "$base_toolbox_command: container $toolbox_container was created from image $toolbox_image" >&3
[ "$toolbox_image" != "" ] 2>&3 && check_image_for_volumes "$toolbox_image"
echo "$base_toolbox_command: trying to start container $toolbox_container" >&3
if ! $prefix_sudo podman start $toolbox_container >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to start container $toolbox_container" >&2
exit 1
fi
if [ "$DBUS_SYSTEM_BUS_ADDRESS" != "" ]; then
set_dbus_system_bus_address="--env DBUS_SYSTEM_BUS_ADDRESS=$DBUS_SYSTEM_BUS_ADDRESS"
fi
set_environment=$(create_environment_options)
echo "$base_toolbox_command: looking for $SHELL in container $toolbox_container" >&3
if $prefix_sudo podman exec $toolbox_container test -f "$SHELL" 2>&3; then
shell_to_exec=$SHELL
else
echo "$base_toolbox_command: $SHELL not found in $toolbox_container; using $shell_to_exec instead" >&3
fi
echo "$base_toolbox_command: trying to exec $shell_to_exec in container $toolbox_container" >&3
# shellcheck disable=SC2016
# for the command passed to capsh
# shellcheck disable=SC2086
$prefix_sudo podman exec \
$set_dbus_system_bus_address \
$set_environment \
--interactive \
--tty \
"$toolbox_container" \
capsh --caps="" -- -c 'cd "$1"; export PS1="$2"; shift 2; exec "$@"' \
/bin/sh \
"$PWD" \
"$toolbox_prompt" \
"$shell_to_exec" -l 2>&3
)
list_images()
(
if ! images_old=$($prefix_sudo podman images \
--filter "label=com.redhat.component=fedora-toolbox" \
--format "{{.Repository}}:{{.Tag}}" 2>&3); then
echo "$base_toolbox_command: failed to list images with com.redhat.component=fedora-toolbox" >&2
return 1
fi
if ! images=$($prefix_sudo podman images \
--filter "label=com.github.debarshiray.toolbox=true" \
--format "{{.Repository}}:{{.Tag}}" 2>&3); then
echo "$base_toolbox_command: failed to list images with com.github.debarshiray.toolbox=true" >&2
return 1
fi
images=$(printf "%s\n%s\n" "$images_old" "$images" | sort 2>&3 | uniq 2>&3)
if ! details=$(images_get_details "$images"); then
return 1
fi
if ! output=$(echo "$details" \
| sed "s/ \{2,\}/\t/g" 2>&3 \
| column --separator "$tab" --table --table-columns "IMAGE ID,IMAGE NAME,CREATED" 2>&3); then
echo "$base_toolbox_command: failed to parse list of images" >&2
return 1
fi
if [ "$output" != "" ]; then
# shellcheck disable=SC2059
printf "${LBC}Images created by toolbox${NC}\n"
echo "$output"
fi
return 0
)
containers_get_details()
(
containers="$1"
if ! echo "$containers" | while read -r container; do
[ "$container" = "" ] 2>&3 && continue
if ! $prefix_sudo podman ps --all \
--filter "name=$container" \
--format "{{.ID}} {{.Names}} {{.Created}} {{.Status}} {{.Image}}" 2>&3; then
echo "$base_toolbox_command: failed to get details for container $container" >&2
return 1
fi
done; then
return 1
fi
return 0
)
list_containers()
(
if ! containers_old=$($prefix_sudo podman ps \
--all \
--filter "label=com.redhat.component=fedora-toolbox" \
--format "{{.Names}}" 2>&3); then
echo "$base_toolbox_command: failed to list containers with com.redhat.component=fedora-toolbox" >&2
return 1
fi
if ! containers=$($prefix_sudo podman ps \
--all \
--filter "label=com.github.debarshiray.toolbox=true" \
--format "{{.Names}}" 2>&3); then
echo "$base_toolbox_command: failed to list containers with com.github.debarshiray.toolbox=true" >&2
return 1
fi
containers=$(printf "%s\n%s\n" "$containers_old" "$containers" | sort 2>&3 | uniq 2>&3)
if ! details=$(containers_get_details "$containers"); then
return 1
fi
if ! output=$(echo "$details" \
| sed "s/ \{2,\}/\t/g" 2>&3 \
| column \
--separator "$tab" \
--table \
--table-columns "CONTAINER ID,CONTAINER NAME,CREATED,STATUS,IMAGE NAME" 2>&3); then
echo "$base_toolbox_command: failed to parse list of containers" >&2
return 1
fi
if [ "$output" != "" ]; then
# shellcheck disable=SC2059
printf "${LBC}Containers created by toolbox${NC}\n"
echo "$output" | head --lines 1 2>&3
echo "$output" | tail --lines +2 2>&3 \
| (
while read -r container; do
id=$(echo "$container" | cut --delimiter " " --fields 1 2>&3)
is_running=$($prefix_sudo podman inspect "$id" --format "{{.State.Running}}" 2>&3)
if $is_running; then
# shellcheck disable=SC2059
printf "${LGC}$container${NC}\n"
else
echo "$container"
fi
done
)
fi
return 0
)
remove_containers()
(
ids=$1
all=$2
force=$3
ret_val=0
$force && force_option="--force"
if $all; then
if ! ids_old=$($prefix_sudo podman ps \
--all \
--filter "label=com.redhat.component=fedora-toolbox" \
--format "{{.ID}}" 2>&3); then
echo "$base_toolbox_command: failed to list containers with com.redhat.component=fedora-toolbox" >&2
return 1
fi
if ! ids=$($prefix_sudo podman ps \
--all \
--filter "label=com.github.debarshiray.toolbox=true" \
--format "{{.ID}}" 2>&3); then
echo "$base_toolbox_command: failed to list containers with com.github.debarshiray.toolbox=true" >&2
return 1
fi
ids=$(printf "%s\n%s\n" "$ids_old" "$ids" | sort 2>&3 | uniq 2>&3)
if [ "$ids" != "" ]; then
ret_val=$(echo "$ids" \
| (
while read -r id; do
if ! $prefix_sudo podman rm $force_option "$id" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove container $id" >&2
ret_val=1
fi
done
echo "$ret_val"
)
)
fi
else
ret_val=$(echo "$ids" \
| sed "s/ \+/\n/g" 2>&3 \
| (
while read -r id; do
if ! labels=$($prefix_sudo podman inspect \
--format "{{.Config.Labels}}" \
--type container \
"$id" 2>&3); then
echo "$base_toolbox_command: failed to inspect $id" >&2
ret_val=1
continue
fi
if ! has_substring "$labels" "com.github.debarshiray.toolbox" \
&& ! has_substring "$labels" "com.redhat.component:fedora-toolbox"; then
echo "$base_toolbox_command: $id is not a toolbox container" >&2
ret_val=1
continue
fi
if ! $prefix_sudo podman rm $force_option "$id" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove container $id" >&2
ret_val=1
fi
done
echo "$ret_val"
)
)
fi
return "$ret_val"
)
remove_images()
(
ids=$1
all=$2
force=$3
ret_val=0
$force && force_option="--force"
if $all; then
if ! ids_old=$($prefix_sudo podman images \
--filter "label=com.redhat.component=fedora-toolbox" \
--format "{{.ID}}" 2>&3); then
echo "$0: failed to list images with com.redhat.component=fedora-toolbox" >&2
return 1
fi
if ! ids=$($prefix_sudo podman images \
--all \
--filter "label=com.github.debarshiray.toolbox=true" \
--format "{{.ID}}" 2>&3); then
echo "$0: failed to list images with com.github.debarshiray.toolbox=true" >&2
return 1
fi
ids=$(printf "%s\n%s\n" "$ids_old" "$ids" | sort 2>&3 | uniq 2>&3)
if [ "$ids" != "" ]; then
ret_val=$(echo "$ids" \
| (
while read -r id; do
if ! $prefix_sudo podman rmi $force_option "$id" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove image $id" >&2
ret_val=1
fi
done
echo "$ret_val"
)
)
fi
else
ret_val=$(echo "$ids" \
| sed "s/ \+/\n/g" 2>&3 \
| (
while read -r id; do
if ! labels=$($prefix_sudo podman inspect \
--format "{{.Labels}}" \
--type image \
"$id" 2>&3); then
echo "$base_toolbox_command: failed to inspect $id" >&2
ret_val=1
continue
fi
if ! has_substring "$labels" "com.github.debarshiray.toolbox" \
&& ! has_substring "$labels" "com.redhat.component:fedora-toolbox"; then
echo "$base_toolbox_command: $id is not a toolbox image" >&2
ret_val=1
continue
fi
if ! $prefix_sudo podman rmi $force_option "$id" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove image $id" >&2
ret_val=1
fi
done
echo "$ret_val"
)
)
fi
return "$ret_val"
)
exit_if_extra_operand()
{
if [ "$1" != "" ]; then
echo "$base_toolbox_command: extra operand '$1'" >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
fi
}
exit_if_missing_argument()
{
if [ "$2" = "" ]; then
echo "$base_toolbox_command: missing argument for '$1'" >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
fi
}
exit_if_non_positive_argument()
{
if ! is_integer "$2"; then
echo "$base_toolbox_command: invalid argument for '$1'" >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
fi
if [ "$2" -le 0 ] 2>&3; then
echo "$base_toolbox_command: invalid argument for '$1'" >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
fi
}
exit_if_unrecognized_option()
{
echo "$base_toolbox_command: unrecognized option '$1'" >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
}
# shellcheck disable=SC2120
forward_to_host()
(
eval "set -- $arguments"
if [ "$DBUS_SYSTEM_BUS_ADDRESS" != "" ]; then
set_dbus_system_bus_address="--env=DBUS_SYSTEM_BUS_ADDRESS=$DBUS_SYSTEM_BUS_ADDRESS"
fi
set_environment=$(create_environment_options)
echo "$base_toolbox_command: forwarding to host:" >&3
echo "$base_toolbox_command: $0" >&3
for i in "$@"; do
echo "$base_toolbox_command: $i" >&3
done
# shellcheck disable=SC2086
flatpak-spawn $set_dbus_system_bus_address $set_environment --host "$0" "$@" 2>&3
)
update_container_and_image_names()
{
[ "$release" = "" ] 2>&3 && release="$release_default"
if [ "$base_toolbox_image" = "" ] 2>&3; then
base_toolbox_image="fedora-toolbox:$release"
else
release=$(image_reference_get_tag "$base_toolbox_image")
[ "$release" = "" ] 2>&3 && release="$release_default"
fi
fgc="f$release"
echo "$base_toolbox_command: Fedora generational core is $fgc" >&3
echo "$base_toolbox_command: base image is $base_toolbox_image" >&3
toolbox_image=$(create_toolbox_image_name)
if ! (
ret_val=$?
if [ "$ret_val" -ne 0 ] 2>&3; then
if [ "$ret_val" -eq 100 ] 2>&3; then
echo "$base_toolbox_command: failed to get the basename of base image $base_toolbox_image" >&2
else
echo "$base_toolbox_command: failed to create an ID for the customized user-specific image" >&2
fi
exit 1
fi
exit 0
); then
exit 1
fi
echo "$base_toolbox_command: customized user-specific image is $toolbox_image" >&3
[ "$toolbox_container" = "" ] && toolbox_container="$toolbox_image"
echo "$base_toolbox_command: container is $toolbox_container" >&3
}
usage()
{
echo "Usage: toolbox [-v | --verbose]"
echo " create [--candidate-registry]"
echo " [--container <name>]"
echo " [--image <name>]"
echo " [--release <release>]"
echo " or: toolbox [-v | --verbose]"
echo " enter [--container <name>]"
echo " [--release <release>]"
echo " or: toolbox [-v | --verbose]"
echo " list [-c | --containers]"
echo " [-i | --images]"
echo " or: toolbox rm [-a | --all]"
echo " [-f | --force] [<container> ...]"
echo " or: toolbox rmi [-a | --all]"
echo " [-f | --force] [<image> ...]"
echo " or: toolbox --help"
}
arguments=$(save_positional_parameters "$@")
release_default=$(get_host_version_id)
toolbox_container_prefix_default="fedora-toolbox-$USER"
toolbox_container_default="$toolbox_container_prefix_default:$release_default"
while has_prefix "$1" -; do
case $1 in
-h | --help )
usage
exit
;;
--sudo )
prefix_sudo="sudo"
;;
-v | --verbose )
exec 3>&2
verbose=true
;;
* )
exit_if_unrecognized_option "$1"
esac
shift
done
if [ -f /run/.containerenv ] 2>&3; then
if ! command -v flatpak-spawn >/dev/null 2>&3; then
echo "$base_toolbox_command: flatpak-spawn not found" >&2
exit 1
fi
echo "$base_toolbox_command: looking for podman PID" >&3
pid_i=$$
while [ "$pid_i" -gt 1 ] 2>&3; do
cmdline=$(sed "s/\x0/ /g" /proc/$pid_i/cmdline 2>&3)
if has_prefix "$cmdline" "podman exec "; then
podman_pid=$pid_i
break
fi
pid_i=$(ps -p $pid_i -o ppid= 2>&3 | sed "s/ //" 2>&3)
if ! is_integer "$pid_i"; then
echo "$base_toolbox_command: invalid parent PID" >&2
break
fi
done
if ! is_integer "$podman_pid"; then
echo "$base_toolbox_command: invalid podman PID" >&2
podman_pid=""
fi
if [ "$podman_pid" = "" ]; then
echo "$base_toolbox_command: cannot be used outside podman exec" >&2
exit 1
fi
echo "$base_toolbox_command: podman PID is $podman_pid" >&3
fi
if [ "$1" = "" ]; then
echo "$base_toolbox_command: missing command" >&2
echo >&2
echo "These are some common commands:" >&2
echo "create Create a new toolbox container" >&2
echo "enter Enter an existing toolbox container" >&2
echo "list List all existing toolbox containers and images" >&2
echo >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
fi
op=$1
shift
case $op in
create )
if is_integer "$podman_pid"; then
# shellcheck disable=SC2119
forward_to_host
else
while has_prefix "$1" -; do
case $1 in
--candidate-registry )
registry=$registry_candidate
;;
--container )
shift
exit_if_missing_argument --container "$1"
toolbox_container=$1
;;
--image )
shift
exit_if_missing_argument --image "$1"
base_toolbox_image=$1
;;
--release )
shift
exit_if_missing_argument --release "$1"
arg=$(echo "$1" | sed "s/^F\|^f//" 2>&3)
exit_if_non_positive_argument --release "$arg"
release=$arg
;;
* )
exit_if_unrecognized_option "$1"
esac
shift
done
exit_if_extra_operand "$1"
update_container_and_image_names
create
fi
exit
;;
enter )
if is_integer "$podman_pid"; then
# shellcheck disable=SC2119
forward_to_host
else
while has_prefix "$1" -; do
case $1 in
--container )
shift
exit_if_missing_argument --container "$1"
toolbox_container=$1
;;
--release )
shift
exit_if_missing_argument --release "$1"
arg=$(echo "$1" | sed "s/^F\|^f//" 2>&3)
exit_if_non_positive_argument --release "$arg"
release=$arg
;;
* )
exit_if_unrecognized_option "$1"
esac
shift
done
exit_if_extra_operand "$1"
update_container_and_image_names
enter
fi
exit
;;
list )
if is_integer "$podman_pid"; then
# shellcheck disable=SC2119
forward_to_host
else
ls_images=false
ls_containers=false
while has_prefix "$1" -; do
case $1 in
-c | --containers )
ls_containers=true
;;
-i | --images )
ls_images=true
;;
* )
exit_if_unrecognized_option "$1"
esac
shift
done
exit_if_extra_operand "$1"
if ! $ls_containers && ! $ls_images; then
ls_containers=true
ls_images=true
fi
if $ls_images; then
if ! images=$(list_images); then
exit 1
fi
fi
if $ls_containers; then
if ! containers=$(list_containers); then
exit 1
fi
fi
$ls_images && [ "$images" != "" ] && echo "$images"
$ls_containers && [ "$containers" != "" ] && echo "$containers"
fi
exit
;;
rm | rmi )
if is_integer "$podman_pid"; then
# shellcheck disable=SC2119
forward_to_host
else
rm_all=false
rm_force=false
while has_prefix "$1" -; do
case $1 in
-a | --all )
rm_all=true
;;
-f | --force )
rm_force=true
;;
* )
exit_if_unrecognized_option "$1"
esac
shift
done
rm_ids=""
if $rm_all; then
exit_if_extra_operand "$1"
else
exit_if_missing_argument "$op" "$1"
while [ "$1" != "" ]; do
rm_ids="$rm_ids $1"
shift
done
fi
rm_ids=$(echo "$rm_ids" | sed "s/^ \+//" 2>&3)
if [ "$op" = "rm" ]; then
remove_containers "$rm_ids" "$rm_all" "$rm_force"
else
remove_images "$rm_ids" "$rm_all" "$rm_force"
fi
fi
exit
;;
* )
echo "$base_toolbox_command: unrecognized command '$op'" >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
esac