#!/bin/sh # # Copyright © 2018 – 2022 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. # # THIS IMPLEMENTATION OF TOOLBOX IS NO LONGER MAINTAINED. # Please, use the Go implementation instead: # https://containertoolbx.org/install/ exec 3>/dev/null arguments="" assume_yes=false base_toolbox_command=$(basename "$0" 2>&3) base_toolbox_image="" cgroups_version="" # Based on the nameRegex value in: # https://github.com/containers/libpod/blob/master/libpod/options.go container_name_regexp="[a-zA-Z0-9][a-zA-Z0-9_.-]*" environment=$(set) environment_variables="COLORTERM \ COLUMNS \ DBUS_SESSION_BUS_ADDRESS \ DBUS_SYSTEM_BUS_ADDRESS \ DESKTOP_SESSION \ DISPLAY \ LANG \ LINES \ SHELL \ SSH_AUTH_SOCK \ TERM \ TOOLBOX_PATH \ USER \ VTE_VERSION \ WAYLAND_DISPLAY \ XAUTHORITY \ 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_command="podman" registry="registry.fedoraproject.org" registry_candidate="candidate-registry.fedoraproject.org" release="" release_default="" spinner_animation="[>----] [=>---] [==>--] [===>-] [====>] [----<] [---<=] [--<==] [-<===] [<====]" spinner_template="toolbox-spinner-XXXXXXXXXX" tab="$(printf '\t')" toolbox_command_path="" toolbox_container="" toolbox_container_default="" toolbox_container_old_v1="" toolbox_container_old_v2="" toolbox_container_prefix_default="" toolbox_image="" toolbox_runtime_directory="$XDG_RUNTIME_DIR"/toolbox user_id_real=$(id -ru 2>&3) verbose=false LGC='\033[1;32m' # Light Green 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 ) ask_for_confirmation() ( default_response="$1" prompt="$2" ret_val=0 while :; do printf "%s " "$prompt" read -r user_response if [ "$user_response" = "" ] 2>&3; then user_response="$default_response" else user_response=$(echo "$user_response" | tr "[:upper:]" "[:lower:]" 2>&3) fi if [ "$user_response" = "no" ] 2>&3 || [ "$user_response" = "n" ] 2>&3; then ret_val=1 break elif [ "$user_response" = "yes" ] 2>&3 || [ "$user_response" = "y" ] 2>&3; then ret_val=0 break fi done return "$ret_val" ) container_name_is_valid() ( name="$1" echo "$name" | grep "^$container_name_regexp$" >/dev/null 2>&3 return "$?" ) container_start() ( container="$1" error_message=$( ($podman_command start "$container" >/dev/null) 2>&1) ret_val="$?" [ "$error_message" != "" ] 2>&3 && echo "$error_message" >&3 if [ "$ret_val" -ne 0 ] 2>&3; then if echo "$error_message" | grep "use system migrate to mitigate" >/dev/null 2>&3; then echo "$base_toolbox_command: checking if 'podman system migrate' supports --new-runtime" >&3 if ! ($podman_command system migrate --help 2>&3 | grep "new-runtime" >/dev/null 2>&3); then echo "$base_toolbox_command: container $container doesn't support cgroups v$cgroups_version" >&2 echo "Update Podman to version 1.6.2 or newer." >&2 return 1 else echo "$base_toolbox_command: 'podman system migrate' supports --new-runtime" >&3 oci_runtime_required="runc" [ "$cgroups_version" -eq 2 ] 2>&3 && oci_runtime_required="crun" echo "$base_toolbox_command: migrating containers to OCI runtime $oci_runtime_required" >&3 if ! $podman_command system migrate --new-runtime "$oci_runtime_required" >/dev/null 2>&3; then echo "$base_toolbox_command: failed to migrate containers to OCI runtime $oci_runtime_required" >&2 echo "Factory reset with: toolbox reset" >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 return 1 fi if ! $podman_command start "$container" >/dev/null 2>&3; then echo "$base_toolbox_command: container $container doesn't support cgroups v$cgroups_version" >&2 echo "Factory reset with: toolbox reset" >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 return 1 fi fi else echo "$base_toolbox_command: failed to start container $container" >&2 return 1 fi fi return 0 ) copy_etc_profile_d_toolbox_to_container() ( container="$1" profile_d_lock="$toolbox_runtime_directory"/profile.d-toolbox.lock # shellcheck disable=SC2174 if ! mkdir --mode 700 --parents "$toolbox_runtime_directory" 2>&3; then echo "$base_toolbox_command: unable to copy $toolbox_runtime_directory/toolbox.sh: runtime directory not created" >&2 return 1 fi exec 5>"$profile_d_lock" if ! flock 5 2>&3; then echo "$base_toolbox_command: unable to copy $toolbox_runtime_directory/toolbox.sh: copy lock not acquired" >&2 return 1 fi if ! [ -f "$toolbox_runtime_directory"/toolbox.sh ] 2>&3; then echo "$base_toolbox_command: $toolbox_runtime_directory/toolbox.sh not found" >&2 return 0 fi echo "$base_toolbox_command: copying $toolbox_runtime_directory/toolbox.sh to container $container" >&3 if ! $podman_command exec \ --user root:root \ "$container" \ sh -c "cp $toolbox_runtime_directory/toolbox.sh /etc/profile.d" sh 2>&3; then echo "$base_toolbox_command: unable to copy $toolbox_runtime_directory/toolbox.sh to container $container" >&2 return 1 fi return 0 ) copy_etc_profile_d_toolbox_to_runtime_directory() ( profile_d_lock="$toolbox_runtime_directory"/profile.d-toolbox.lock if ! [ -f /etc/profile.d/toolbox.sh ] 2>&3; then echo "$base_toolbox_command: /etc/profile.d/toolbox.sh not found" >&2 return 0 fi # shellcheck disable=SC2174 if ! mkdir --mode 700 --parents "$toolbox_runtime_directory" 2>&3; then echo "$base_toolbox_command: unable to copy /etc/profile.d/toolbox.sh: runtime directory not created" >&2 return 1 fi exec 5>"$profile_d_lock" if ! flock 5 2>&3; then echo "$base_toolbox_command: unable to copy /etc/profile.d/toolbox.sh: copy lock not acquired" >&2 return 1 fi echo "$base_toolbox_command: copying /etc/profile.d/toolbox.sh to $toolbox_runtime_directory" >&3 if ! cp /etc/profile.d/toolbox.sh "$toolbox_runtime_directory" 2>&3; then echo "$base_toolbox_command: unable to copy /etc/profile.d/toolbox.sh to $toolbox_runtime_directory" >&2 return 1 fi return 0 ) create_enter_command() ( container="$1" if [ "$container" = "$toolbox_container_default" ] 2>&3; then echo "$base_toolbox_command enter" elif [ "$container" = "$toolbox_container_prefix_default-$release" ] 2>&3; then echo "$base_toolbox_command enter --release $release" else echo "$base_toolbox_command enter --container $container" fi ) create_environment_options() ( columns="" lines="" if terminal_size=$(stty size 2>&3); then columns=$(echo "$terminal_size" | cut --delimiter " " --fields 2 2>&3) if ! is_integer "$columns"; then echo "$base_toolbox_command: failed to parse the number of columns from the terminal size" >&3 columns="" fi lines=$(echo "$terminal_size" | cut --delimiter " " --fields 1 2>&3) if ! is_integer "$lines"; then echo "$base_toolbox_command: failed to parse the number of lines from the terminal size" >&3 lines="" fi else echo "$base_toolbox_command: failed to read terminal size" >&3 fi 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 if echo "$environment" | grep "^$variable" >/dev/null 2>&3; then eval value="$""$variable" echo "$base_toolbox_command: $variable=$value" >&3 environment_options="$environment_options --env=$variable=$value" else echo "$base_toolbox_command: $variable is unset" >&3 fi done if ! (echo "$environment_options" | grep COLUMNS >/dev/null 2>&3) && [ "$columns" != "" ] 2>&3; then environment_options="$environment_options --env=COLUMNS=$columns" fi if ! (echo "$environment_options" | grep LINES >/dev/null 2>&3) && [ "$lines" != "" ] 2>&3; then environment_options="$environment_options --env=LINES=$lines" fi 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_container_name() ( image="$1" basename=$(image_reference_get_basename "$image") if [ "$basename" = "" ] 2>&3; then return 100 fi tag=$(image_reference_get_tag "$image") if [ "$tag" = "" ] 2>&3; then return 101 fi echo "$basename-$tag" return 0 ) 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=$($podman_command 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:latest" else echo "$basename-$USER:$tag" fi return 0 ) enter_print_container_not_found() ( container="$1" echo "$base_toolbox_command: container $container not found" >&2 echo "Use the 'create' command to create a toolbox." >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 ) get_cgroups_version() ( version=1 if ! mounts=$(mount 2>&3); then echo "$base_toolbox_command: failed to detect cgroups version: couldn't list mount points" >&2 return 1 fi if ! (echo "$mounts" | grep "^cgroup " >/dev/null 2>&3) && (echo "$mounts" | grep "^cgroup2 " >/dev/null 2>&3); then version=2 fi echo "$version" return 0 ) get_group_for_sudo() ( group="" if getent group sudo >/dev/null 2>&3; then group="sudo" elif getent group wheel >/dev/null 2>&3; then group="wheel" else return 1 fi echo "$group" return 0 ) get_host_id() ( # shellcheck disable=SC1091 . /usr/lib/os-release echo "$ID" ) get_host_variant_id() ( # shellcheck disable=SC1091 . /usr/lib/os-release echo "$VARIANT_ID" ) get_host_version_id() ( # shellcheck disable=SC1091 . /usr/lib/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 ! $podman_command 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 echo done; then return 1 fi return 0 ) is_etc_profile_d_toolbox_a_bind_mount() { container="$1" $podman_command inspect --format "[{{range .Mounts}}{{.Dst}} {{end}}]" --type container "$container" 2>&3 \ | grep /etc/profile.d/toolbox.sh >/dev/null 2>/dev/null 2>&3 return "$?" } list_container_names() ( if ! containers_old=$($podman_command 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=$($podman_command 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 printf "%s\n%s\n" "$containers_old" "$containers" | sort 2>&3 | uniq 2>&3 return 0 ) mount_bind() ( source="$1" target="$2" mount_flags="$3" mount_o="" ! [ -d "$source" ] 2>&3 && ! [ -f "$source" ] 2>&3 && return 0 if [ -d "$source" ] 2>&3; then echo "$base_toolbox_command: creating $target" >&3 if ! mkdir --parents "$target" 2>&3; then echo "$base_toolbox_command: failed to create $target" >&2 return 1 fi fi echo "$base_toolbox_command: binding $target to $source" >&3 [ "$mount_flags" = "" ] 2>&3 || mount_o="-o $mount_flags" # shellcheck disable=SC2086 if ! mount --rbind $mount_o "$source" "$target" 2>&3; then echo "$base_toolbox_command: failed to bind $target to $source" >&2 return 1 fi return 0 ) pull_base_toolbox_image() ( domain="" has_domain=false prompt_for_download=true pull_image=false if image_reference_can_be_id "$base_toolbox_image"; then echo "$base_toolbox_command: looking for image $base_toolbox_image" >&3 if $podman_command image exists "$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 $podman_command image exists "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 $podman_command image exists "$base_toolbox_image_full" >/dev/null 2>&3; then return 0 fi domain=$(image_reference_get_domain "$base_toolbox_image_full") if $assume_yes || [ "$domain" = "localhost" ] 2>&3; then prompt_for_download=false pull_image=true fi if $prompt_for_download; then echo "Image required to create toolbox container." prompt=$(printf "Download %s (500MB)? [y/N]:" "$base_toolbox_image_full") if ask_for_confirmation "n" "$prompt"; then pull_image=true else pull_image=false fi fi if ! $pull_image; then return 1 fi echo "$base_toolbox_command: pulling image $base_toolbox_image_full" >&3 if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then spinner_message="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 $podman_command pull "$base_toolbox_image_full" >/dev/null 2>&3 ret_val=$? if [ "$spinner_directory" != "" ]; then spinner_stop "$spinner_directory" fi if [ "$ret_val" -ne 0 ] 2>&3; then echo "$base_toolbox_command: failed to pull base image $base_toolbox_image" >&2 fi return $ret_val ) 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 4>"$unshare_directory/map" if ! flock 4 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 GID and 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: GID map of user namespace:" >&3 cat /proc/"$unshare_pid"/gid_map 1>&3 2>&3 echo "$base_toolbox_command: UID map of user namespace:" >&3 cat /proc/"$unshare_pid"/uid_map 1>&3 2>&3 if ! flock --unlock 4 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" dbus_system_bus_address="unix:path=/var/run/dbus/system_bus_socket" home_link="" kcm_socket="" kcm_socket_bind="" media_link="" media_path_bind="" mnt_link="" mnt_path_bind="" run_media_path_bind="" toolbox_profile_bind="" ulimit_host="" usr_mount_destination_flags="ro" # 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_socket=${kcm_socket_listen%" (Stream)"} kcm_socket_bind="--volume $kcm_socket:$kcm_socket" fi echo "$base_toolbox_command: checking if 'podman create' supports --ulimit host" >&3 if man podman-create 2>&3 | grep "You can pass host" >/dev/null 2>&3; then echo "$base_toolbox_command: 'podman create' supports --ulimit host" >&3 ulimit_host="--ulimit host" fi if ! pull_base_toolbox_image; then return 1 fi if image_reference_has_domain "$base_toolbox_image"; then base_toolbox_image_full="$base_toolbox_image" else if ! base_toolbox_image_full=$($podman_command 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 return 1 fi echo "$base_toolbox_command: base image $base_toolbox_image resolved to $base_toolbox_image_full" >&3 fi echo "$base_toolbox_command: checking if container $toolbox_container already exists" >&3 enter_command=$(create_enter_command "$toolbox_container") if $podman_command container exists "$toolbox_container" >/dev/null 2>&3; then echo "$base_toolbox_command: container $toolbox_container already exists" >&2 echo "Enter with: $enter_command" >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 return 1 fi if [ -f /etc/profile.d/toolbox.sh ] 2>&3; then toolbox_profile_bind="--volume /etc/profile.d/toolbox.sh:/etc/profile.d/toolbox.sh:ro" elif [ -f /usr/share/profile.d/toolbox.sh ] 2>&3; then toolbox_profile_bind="--volume /usr/share/profile.d/toolbox.sh:/etc/profile.d/toolbox.sh:ro" else echo "$base_toolbox_command: failed to bind mount toolbox.sh" >&3 fi if [ -d /media ] 2>&3; then echo "$base_toolbox_command: checking if /media is a symbolic link to /run/media" >&3 if [ "$(readlink /media)" = run/media ] 2>&3; then echo "$base_toolbox_command: /media is a symbolic link to /run/media" >&3 media_link="--media-link" else media_path_bind="--volume /media:/media:rslave" fi fi echo "$base_toolbox_command: checking if /mnt is a symbolic link to /var/mnt" >&3 if [ "$(readlink /mnt)" = var/mnt ] 2>&3; then echo "$base_toolbox_command: /mnt is a symbolic link to /var/mnt" >&3 mnt_link="--mnt-link" else mnt_path_bind="--volume /mnt:/mnt:rslave" fi if [ -d /run/media ] 2>&3; then run_media_path_bind="--volume /run/media:/run/media:rslave" fi echo "$base_toolbox_command: checking if /usr is mounted read-only or read-write" >&3 if ! usr_mount_point=$(df --output=target /usr | tail --lines 1 2>&3); then echo "$base_toolbox_command: failed to get the mount-point of /usr" >&2 else echo "$base_toolbox_command: mount-point of /usr is $usr_mount_point" >&3 if ! usr_mount_source_flags=$(findmnt --noheadings --output OPTIONS "$usr_mount_point" 2>&3); then echo "$base_toolbox_command: failed to get the mount options of $usr_mount_point" >&2 else echo "$base_toolbox_command: mount flags of /usr on the host are $usr_mount_source_flags" >&3 if echo "$usr_mount_source_flags" | grep --invert-match "ro" >/dev/null 2>&3; then usr_mount_destination_flags="rw" fi fi fi if ! home_canonical=$(readlink --canonicalize "$HOME" 2>&3); then echo "$base_toolbox_command: failed to canonicalize $HOME" >&2 return 1 fi echo "$base_toolbox_command: $HOME canonicalized to $home_canonical" >&3 echo "$base_toolbox_command: checking if /home is a symbolic link to /var/home" >&3 if [ "$(readlink /home)" = var/home ] 2>&3; then echo "$base_toolbox_command: /home is a symbolic link to /var/home" >&3 home_link="--home-link" fi echo "$base_toolbox_command: calling org.freedesktop.Flatpak.SessionHelper.RequestSession" >&3 if ! gdbus call \ --session \ --dest org.freedesktop.Flatpak \ --object-path /org/freedesktop/Flatpak/SessionHelper \ --method org.freedesktop.Flatpak.SessionHelper.RequestSession >/dev/null 2>&3; then echo "$base_toolbox_command: failed to call org.freedesktop.Flatpak.SessionHelper.RequestSession" >&2 exit 1 fi echo "$base_toolbox_command: creating container $toolbox_container" >&3 if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then spinner_message="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 $podman_command create \ --dns none \ --env TOOLBOX_PATH="$TOOLBOX_PATH" \ --hostname toolbox \ --ipc host \ --label "com.github.containers.toolbox=true" \ --label "com.github.debarshiray.toolbox=true" \ --name $toolbox_container \ --network host \ --no-hosts \ --pid host \ --privileged \ --security-opt label=disable \ $ulimit_host \ --userns=keep-id \ --user root:root \ $kcm_socket_bind \ $media_path_bind \ $mnt_path_bind \ $run_media_path_bind \ $toolbox_profile_bind \ --volume "$TOOLBOX_PATH":/usr/bin/toolbox:ro \ --volume "$XDG_RUNTIME_DIR":"$XDG_RUNTIME_DIR" \ --volume "$XDG_RUNTIME_DIR"/.flatpak-helper/monitor:/run/host/monitor \ --volume "$dbus_system_bus_path":"$dbus_system_bus_path" \ --volume "$home_canonical":"$home_canonical":rslave \ --volume /etc:/run/host/etc \ --volume /dev:/dev:rslave \ --volume /run:/run/host/run:rslave \ --volume /tmp:/run/host/tmp:rslave \ --volume /usr:/run/host/usr:"$usr_mount_destination_flags",rslave \ --volume /var:/run/host/var:rslave \ "$base_toolbox_image_full" \ toolbox --verbose init-container \ --home "$HOME" \ $home_link \ $media_link \ $mnt_link \ --monitor-host \ --shell "$SHELL" \ --uid "$user_id_real" \ --user "$USER" >/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 return 1 fi if ! $enter_command_skip; then echo "Created container: $toolbox_container" echo "Enter with: $enter_command" fi return 0 ) enter() ( emit_escape_sequence=false host_id=$(get_host_id) host_variant_id=$(get_host_variant_id) if [ "$host_id" = "fedora" ] 2>&3 \ && { [ "$host_variant_id" = "silverblue" ] 2>&3 || [ "$host_variant_id" = "workstation" ] 2>&3; }; then emit_escape_sequence=true fi run "$emit_escape_sequence" true false "$SHELL" -l ) init_container() { init_container_home="$1" init_container_home_link="$2" init_container_media_link="$3" init_container_mnt_link="$4" init_container_monitor_host="$5" init_container_shell="$6" init_container_uid="$7" init_container_user="$8" if [ "$XDG_RUNTIME_DIR" = "" ] 2>&3; then echo "$base_toolbox_command: XDG_RUNTIME_DIR is unset" >&3 XDG_RUNTIME_DIR=/run/user/"$init_container_uid" echo "$base_toolbox_command: XDG_RUNTIME_DIR set to $XDG_RUNTIME_DIR" >&3 toolbox_runtime_directory="$XDG_RUNTIME_DIR"/toolbox fi init_container_initialized_stamp="$toolbox_runtime_directory"/container-initialized-"$$" echo "$base_toolbox_command: creating /run/.toolboxenv" >&3 if ! touch /run/.toolboxenv 2>&3; then echo "$base_toolbox_command: failed to create /run/.toolboxenv" >&2 return 1 fi if $init_container_monitor_host; then working_directory="$PWD" if [ -d /run/host/etc ] 2>&3; then if ! readlink /etc/host.conf >/dev/null 2>&3; then echo "$base_toolbox_command: redirecting /etc/host.conf to /run/host/etc/host.conf" >&3 if ! (cd /etc 2>&3 \ && rm --force host.conf 2>&3 \ && ln --symbolic /run/host/etc/host.conf host.conf 2>&3); then echo "$base_toolbox_command: failed to redirect /etc/host.conf to /run/host/etc/host.conf" >&2 return 1 fi fi if ! readlink /etc/hosts >/dev/null 2>&3; then echo "$base_toolbox_command: redirecting /etc/hosts to /run/host/etc/hosts" >&3 if ! (cd /etc 2>&3 \ && rm --force hosts 2>&3 \ && ln --symbolic /run/host/etc/hosts hosts 2>&3); then echo "$base_toolbox_command: failed to redirect /etc/hosts to /run/host/etc/hosts" >&2 return 1 fi fi if ! readlink /etc/resolv.conf >/dev/null 2>&3; then echo "$base_toolbox_command: redirecting /etc/resolv.conf to /run/host/etc/resolv.conf" >&3 if ! (cd /etc 2>&3 \ && rm --force resolv.conf 2>&3 \ && ln --symbolic /run/host/etc/resolv.conf resolv.conf 2>&3); then echo "$base_toolbox_command: failed to redirect /etc/resolv.conf to /run/host/etc/resolv.conf" \ >&2 return 1 fi fi if ! mount_bind /run/host/etc/machine-id /etc/machine-id ro; then return 1 fi if ! mount_bind /run/host/run/libvirt /run/libvirt; then return 1 fi if ! mount_bind /run/host/run/systemd/journal /run/systemd/journal; then return 1 fi if [ -d /sys/fs/selinux ] 2>&3; then if ! mount_bind /usr/share/empty /sys/fs/selinux; then return 1 fi fi if ! mount_bind /run/host/var/lib/flatpak /var/lib/flatpak ro; then return 1 fi if ! mount_bind /run/host/var/log/journal /var/log/journal ro; then return 1 fi if ! mount_bind /run/host/var/mnt /var/mnt rslave; then return 1 fi fi if [ -d /run/host/monitor ] 2>&3; then if ! localtime_target=$(readlink /etc/localtime >/dev/null 2>&3) \ || [ "$localtime_target" != "/run/host/monitor/localtime" ] 2>&3; then echo "$base_toolbox_command: redirecting /etc/localtime to /run/host/monitor/localtime" >&3 if ! (cd /etc 2>&3 \ && rm --force localtime 2>&3 \ && ln --symbolic /run/host/monitor/localtime localtime 2>&3); then echo "$base_toolbox_command: failed to redirect /etc/localtime to /run/host/monitor/localtime" \ >&2 return 1 fi fi if ! readlink /etc/timezone >/dev/null 2>&3; then echo "$base_toolbox_command: redirecting /etc/timezone to /run/host/monitor/timezone" >&3 if ! (cd /etc 2>&3 \ && rm --force timezone 2>&3 \ && ln --symbolic /run/host/monitor/timezone timezone 2>&3); then echo "$base_toolbox_command: failed to redirect /etc/timezone to /run/host/monitor/timezone" >&2 return 1 fi fi fi if ! cd "$working_directory" 2>&3; then echo "$base_toolbox_command: failed to restore working directory" >&2 fi fi if $init_container_media_link && ! readlink /media >/dev/null 2>&3; then echo "$base_toolbox_command: making /media a symbolic link to /run/media" >&3 # shellcheck disable=SC2174 if ! (rmdir /media 2>&3 \ && mkdir --mode 0755 --parents /run/media 2>&3 \ && ln --symbolic run/media /media 2>&3); then echo "$base_toolbox_command: failed to make /media a symbolic link" >&2 return 1 fi fi if $init_container_mnt_link && ! readlink /mnt >/dev/null 2>&3; then echo "$base_toolbox_command: making /mnt a symbolic link to /var/mnt" >&3 # shellcheck disable=SC2174 if ! (rmdir /mnt 2>&3 \ && mkdir --mode 0755 --parents /var/mnt 2>&3 \ && ln --symbolic var/mnt /mnt 2>&3); then echo "$base_toolbox_command: failed to make /mnt a symbolic link" >&2 return 1 fi fi if ! id -u "$init_container_user" >/dev/null 2>&3; then if $init_container_home_link ; then echo "$base_toolbox_command: making /home a symlink" >&3 # shellcheck disable=SC2174 if ! (rmdir /home 2>&3 \ && mkdir --mode 0755 --parents /var/home 2>&3 \ && ln --symbolic var/home /home 2>&3); then echo "$base_toolbox_command: failed to make /home a symlink" >&2 return 1 fi fi if ! groups=$(get_group_for_sudo); then echo "$base_toolbox_command: failed to add user $init_container_user: group for sudo not found" >&2 return 1 fi echo "$base_toolbox_command: adding user $init_container_user with UID $init_container_uid" >&3 if ! useradd \ --home-dir "$init_container_home" \ --no-create-home \ --shell "$init_container_shell" \ --uid "$init_container_uid" \ --groups "$groups" \ "$init_container_user" >/dev/null 2>&3; then echo "$base_toolbox_command: failed to add user $init_container_user with UID $init_container_uid" >&2 return 1 fi echo "$base_toolbox_command: removing password for user $init_container_user" >&3 if ! passwd --delete "$init_container_user" >/dev/null 2>&3; then echo "$base_toolbox_command: failed to remove password for user $init_container_user" >&2 return 1 fi echo "$base_toolbox_command: removing password for user root" >&3 if ! passwd --delete root >/dev/null 2>&3; then echo "$base_toolbox_command: failed to remove password for user root" >&2 return 1 fi fi if [ -d /etc/krb5.conf.d ] 2>&3 && ! [ -f /etc/krb5.conf.d/kcm_default_ccache ] 2>&3; then echo "$base_toolbox_command: setting KCM as the default Kerberos credential cache" >&3 cat </etc/krb5.conf.d/kcm_default_ccache 2>&3 # Written by Toolbox # https://github.com/containers/toolbox # # # To disable the KCM credential cache, comment out the following lines. [libdefaults] default_ccache_name = KCM: EOF ret_val=$? if [ "$ret_val" -ne 0 ] 2>&3; then echo "$base_toolbox_command: failed to set KCM as the default Kerberos credential cache" >&2 return 1 fi fi echo "$base_toolbox_command: finished initializing container" >&3 if ! touch "$init_container_initialized_stamp" 2>&3; then echo "$base_toolbox_command: failed to create initialization stamp" >&2 return 1 fi echo "$base_toolbox_command: going to sleep" >&3 exec sleep +Inf } run() ( emit_escape_sequence="$1" fallback_to_bash="$2" pedantic="$3" program="$4" shift 4 create_toolbox_container=false prompt_for_create=true echo "$base_toolbox_command: checking if container $toolbox_container exists" >&3 if ! $podman_command container exists "$toolbox_container" 2>&3; then echo "$base_toolbox_command: container $toolbox_container not found" >&3 if $podman_command container exists "$toolbox_container_old_v1" 2>&3; then echo "$base_toolbox_command: container $toolbox_container_old_v1 found" >&3 # shellcheck disable=SC2030 toolbox_container="$toolbox_container_old_v1" elif $podman_command container exists "$toolbox_container_old_v2" 2>&3; then echo "$base_toolbox_command: container $toolbox_container_old_v2 found" >&3 # shellcheck disable=SC2030 toolbox_container="$toolbox_container_old_v2" else if $pedantic; then enter_print_container_not_found "$toolbox_container" exit 1 fi if ! containers=$(list_container_names); then enter_print_container_not_found "$toolbox_container" exit 1 fi containers_count=$(echo "$containers" | grep --count . 2>&3) if ! is_integer "$containers_count"; then enter_print_container_not_found "$toolbox_container" exit 1 fi echo "$base_toolbox_command: found $containers_count containers" >&3 if [ "$containers_count" -eq 0 ] 2>&3; then if $assume_yes; then create_toolbox_container=true prompt_for_create=false fi if $prompt_for_create; then prompt="No toolbox containers found. Create now? [y/N]" if ask_for_confirmation "n" "$prompt"; then create_toolbox_container=true else create_toolbox_container=false fi fi if ! $create_toolbox_container; then echo "A container can be created later with the 'create' command." >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 exit 1 fi if ! update_container_and_image_names; then exit 1 fi if ! create true; then exit 1 fi elif [ "$containers_count" -eq 1 ] 2>&3 \ && [ "$toolbox_container" = "$toolbox_container_default" ] 2>&3; then echo "$base_toolbox_command: container $toolbox_container not found" >&2 toolbox_container=$(echo "$containers" | grep . 2>&3 | head --lines 1 2>&3) echo "Entering container $toolbox_container instead." >&2 echo "Use the 'create' command to create a different toolbox." >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 else echo "$base_toolbox_command: container $toolbox_container not found" >&2 echo "Use the '--container' option to select a toolbox." >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 exit 1 fi fi fi echo "$base_toolbox_command: calling org.freedesktop.Flatpak.SessionHelper.RequestSession" >&3 if ! gdbus call \ --session \ --dest org.freedesktop.Flatpak \ --object-path /org/freedesktop/Flatpak/SessionHelper \ --method org.freedesktop.Flatpak.SessionHelper.RequestSession >/dev/null 2>&3; then echo "$base_toolbox_command: failed to call org.freedesktop.Flatpak.SessionHelper.RequestSession" >&2 exit 1 fi echo "$base_toolbox_command: starting container $toolbox_container" >&3 if is_etc_profile_d_toolbox_a_bind_mount "$toolbox_container"; then echo "$base_toolbox_command: /etc/profile.d/toolbox.sh already mounted in container $toolbox_container" >&3 if ! container_start "$toolbox_container"; then exit 1 fi else echo "$base_toolbox_command: /etc/profile.d/toolbox.sh not mounted in container $toolbox_container" >&3 if ! copy_etc_profile_d_toolbox_to_runtime_directory; then exit 1 fi if ! container_start "$toolbox_container"; then exit 1 fi if ! copy_etc_profile_d_toolbox_to_container "$toolbox_container"; then exit 1 fi fi echo "$base_toolbox_command: inspecting entry point of container $toolbox_container" >&3 if ! entry_point=$($podman_command inspect --format "{{index .Config.Cmd 0}}" --type container "$toolbox_container" 2>&3); then echo "$base_toolbox_command: failed to inspect entry point of container $toolbox_container" >&2 exit 1 fi echo "$base_toolbox_command: entry point of container $toolbox_container is $entry_point" >&3 if [ "$entry_point" = "toolbox" ] 2>&3; then echo "$base_toolbox_command: waiting for container $toolbox_container to finish initializing" >&3 if ! entry_point_pid=$($podman_command inspect --format "{{.State.Pid}}" --type container "$toolbox_container" 2>&3); then echo "$base_toolbox_command: failed to inspect entry point PID of container $toolbox_container" >&2 exit 1 fi if ! is_integer "$entry_point_pid"; then echo "$base_toolbox_command: failed to parse entry point PID of container $toolbox_container" >&2 exit 1 fi if [ "$entry_point_pid" -le 0 ] 2>&3; then echo "$base_toolbox_command: invalid entry point PID of container $toolbox_container" >&2 exit 1 fi container_initialized_stamp="$toolbox_runtime_directory/container-initialized-$entry_point_pid" container_initialized_timeout=25 #s i=0 while ! [ -f "$container_initialized_stamp" ] 2>&3; do sleep 1 2>&3 i=$((i + 1)) if [ "$i" -eq "$container_initialized_timeout" ] 2>&3; then echo "$base_toolbox_command: failed to initialize container $toolbox_container" >&2 exit 1 fi done else echo "$base_toolbox_command: container $toolbox_container uses deprecated features" >&2 echo "Consider recreating it with Toolbox version 0.0.17 or newer." >&2 fi if ! $podman_command exec --user root:root "$toolbox_container" touch /run/.toolboxenv 2>&3; then echo "$base_toolbox_command: failed to create /run/.toolboxenv in container $toolbox_container" >&2 exit 1 fi set_environment=$(create_environment_options) echo "$base_toolbox_command: looking for $program in container $toolbox_container" >&3 # shellcheck disable=SC2016 if ! $podman_command exec \ --user "$USER" \ "$toolbox_container" \ sh -c 'command -v "$1"' sh "$program" >/dev/null 2>&3; then if $fallback_to_bash; then echo "$base_toolbox_command: $program not found in $toolbox_container; using /bin/bash instead" >&3 program=/bin/bash else echo "$base_toolbox_command: command '$program' not found in container $toolbox_container" >&2 exit 127 fi fi echo "$base_toolbox_command: running in container $toolbox_container:" >&3 echo "$base_toolbox_command: $program" >&3 for i in "$@"; do echo "$base_toolbox_command: $i" >&3 done $emit_escape_sequence && printf "\033]777;container;push;%s;toolbox\033\\" "$toolbox_container" # shellcheck disable=SC2016 # for the command passed to capsh # shellcheck disable=SC2086 $podman_command exec \ --interactive \ --tty \ --user "$USER" \ --workdir "$PWD" \ $set_environment \ "$toolbox_container" \ capsh --caps="" -- -c 'exec "$@"' /bin/sh "$program" "$@" 2>&3 ret_val="$?" $emit_escape_sequence && printf "\033]777;container;pop;;\033\\" if [ "$ret_val" -eq 127 ] 2>&3; then # shellcheck disable=SC2016 if ! $podman_command exec \ --user "$USER" \ "$toolbox_container" \ sh -c 'test -d "$1"' sh "$PWD" >/dev/null 2>&3; then echo "$base_toolbox_command: directory '$PWD' not found in container $toolbox_container" >&2 fi fi exit "$ret_val" ) help() ( to_help_command="$1" if [ "$to_help_command" = "" ] 2>&3 || [ "$to_help_command" = "$base_toolbox_command" ] 2>&3; then exec man toolbox 2>&1 fi exec man toolbox-"$to_help_command" 2>&1 ) list_images() ( output="" if ! images_old=$($podman_command 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=$($podman_command 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 [ "$details" != "" ] 2>&3; then table_data=$(printf "%s\t%s\t%s\n" "IMAGE ID" "IMAGE NAME" "CREATED"; echo "$details") if ! output=$(echo "$table_data" | sed "s/ \{2,\}/\t/g" 2>&3 | column -s "$tab" -t 2>&3); then echo "$base_toolbox_command: failed to parse list of images" >&2 return 1 fi fi if [ "$output" != "" ]; then echo "$output" fi return 0 ) containers_get_details() ( containers="$1" if ! echo "$containers" | while read -r container; do [ "$container" = "" ] 2>&3 && continue if ! $podman_command 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() ( output="" if ! containers=$(list_container_names); then return 1 fi if ! details=$(containers_get_details "$containers"); then return 1 fi if [ "$details" != "" ] 2>&3; then table_data=$(printf "%s\t%s\t%s\t%s\t%s\n" "CONTAINER ID" "CONTAINER NAME" "CREATED" "STATUS" "IMAGE NAME" echo "$details") if ! output=$(echo "$table_data" | sed "s/ \{2,\}/\t/g" 2>&3 | column -s "$tab" -t 2>&3); then echo "$base_toolbox_command: failed to parse list of containers" >&2 return 1 fi fi if [ "$output" != "" ]; then 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=$($podman_command 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 ) migrate() ( configuration_directory="$HOME/.config/toolbox" migrate_stamp="$configuration_directory/podman-system-migrate" migrate_lock="$toolbox_runtime_directory"/migrate.lock if ! version=$($podman_command version --format "{{.Version}}" 2>&3); then echo "$base_toolbox_command: unable to migrate containers: Podman version couldn't be read" >&2 return 1 fi echo "$base_toolbox_command: current Podman version is $version" >&3 if ! mkdir --parents "$configuration_directory" 2>&3; then echo "$base_toolbox_command: unable to migrate containers: configuration directory not created" >&2 return 1 fi # shellcheck disable=SC2174 if ! mkdir --mode 700 --parents "$toolbox_runtime_directory" 2>&3; then echo "$base_toolbox_command: unable to migrate containers: runtime directory not created" >&2 return 1 fi exec 5>"$migrate_lock" if ! flock 5 2>&3; then echo "$base_toolbox_command: unable to migrate containers: migration lock not acquired" >&3 return 1 fi if [ -f "$migrate_stamp" ] 2>&3; then if grep "$version" "$migrate_stamp" >/dev/null 2>&3; then echo "$base_toolbox_command: migration not needed: Podman version $version is unchanged" >&3 return 0 fi if ! version_old=$(printf "%s\n" "$version" \ | cat "$migrate_stamp" - 2>&3 \ | sort --version-sort 2>&3 \ | head --lines 1 2>&3); then echo "$base_toolbox_command: unable to migrate containers: Podman versions couldn't be sorted" >&2 return 1 fi if [ "$version" = "$version_old" ] 2>&3; then echo "$base_toolbox_command: migration not needed: Podman version $version is old" >&3 return 0 fi fi if ! $podman_command system migrate >/dev/null 2>&3; then echo "$base_toolbox_command: unable to migrate containers" >&2 return 1 fi echo "$version" >"$migrate_stamp" return 0 ) remove_containers() ( ids=$1 all=$2 force=$3 ret_val=0 $force && force_option="--force" if $all; then if ! ids_old=$($podman_command 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=$($podman_command 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 ! $podman_command 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=$($podman_command 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 ! $podman_command 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=$($podman_command 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=$($podman_command 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 ! $podman_command 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=$($podman_command 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 ! $podman_command 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" ) 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 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() ( if ! command -v flatpak-spawn >/dev/null 2>&3; then echo "$base_toolbox_command: flatpak-spawn not found" >&2 return 1 fi eval "set -- $arguments" set_environment=$(create_environment_options) echo "$base_toolbox_command: forwarding to host:" >&3 echo "$base_toolbox_command: $TOOLBOX_PATH" >&3 for i in "$@"; do echo "$base_toolbox_command: $i" >&3 done # shellcheck disable=SC2086 flatpak-spawn $set_environment --host "$TOOLBOX_PATH" "$@" 2>&3 ret_val="$?" return "$ret_val" ) 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 return 1 fi # shellcheck disable=SC2031 if [ "$toolbox_container" = "" ]; then toolbox_container=$(create_toolbox_container_name "$base_toolbox_image") 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 image $base_toolbox_image" >&2 elif [ "$ret_val" -eq 101 ] 2>&3; then echo "$base_toolbox_command: failed to get the tag of image $base_toolbox_image" >&2 else echo "$base_toolbox_command: failed to create a name for the toolbox container" >&2 fi exit 1 fi exit 0 ); then return 1 fi if ! container_name_is_valid "$toolbox_container"; then echo "$base_toolbox_command: generated container name $toolbox_container is invalid" >&2 echo "Container names must match '$container_name_regexp'." >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 return 1 fi toolbox_container_old_v1=$(create_toolbox_container_name "$toolbox_image") 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 image $toolbox_image" >&2 elif [ "$ret_val" -eq 101 ] 2>&3; then echo "$base_toolbox_command: failed to get the tag of image $toolbox_image" >&2 else echo "$base_toolbox_command: failed to create a name for the toolbox container" >&2 fi exit 1 fi exit 0 ); then return 1 fi toolbox_container_old_v2="$toolbox_image" fi echo "$base_toolbox_command: container is $toolbox_container" >&3 return 0 } arguments=$(save_positional_parameters "$@") echo "THIS IMPLEMENTATION OF TOOLBOX IS NO LONGER MAINTAINED." printf "Please, use the Go implementation instead: " # shellcheck disable=SC1003 printf '\033]8;;https://containertoolbx.org/install/\033\\containertoolbx.org/install\033]8;;\033\\' printf "\n" echo host_id=$(get_host_id) if [ "$host_id" = "fedora" ] 2>&3; then release_default=$(get_host_version_id) else release_default="30" fi toolbox_container_prefix_default="fedora-toolbox" toolbox_container_default="$toolbox_container_prefix_default-$release_default" while has_prefix "$1" -; do case $1 in --assumeyes | -y ) assume_yes=true ;; -h | --help ) if [ -f /run/.containerenv ] 2>&3; then if ! [ -f /run/.toolboxenv ] 2>&3; then echo "$base_toolbox_command: this is not a toolbox container" >&2 exit 1 fi # shellcheck disable=SC2119 forward_to_host exit fi help "$2" exit ;; -v | --verbose ) exec 3>&2 verbose=true ;; -vv | --very-verbose ) exec 3>&2 podman_command="podman --log-level debug" verbose=true ;; * ) exit_if_unrecognized_option "$1" esac shift done echo "$base_toolbox_command: running as real user ID $user_id_real" >&3 if ! toolbox_command_path=$(realpath "$0" 2>&3); then echo "$base_toolbox_command: failed to resolve absolute path to $0" >&2 exit 1 fi echo "$base_toolbox_command: resolved absolute path for $0 to $toolbox_command_path" >&3 if [ -f /run/.containerenv ] 2>&3; then if [ "$TOOLBOX_PATH" = "" ] 2>&3; then echo "$base_toolbox_command: TOOLBOX_PATH not set" >&2 exit 1 fi else if [ "$user_id_real" -ne 0 ] 2>&3; then echo "$base_toolbox_command: checking if /etc/subgid and /etc/subuid have entries for user $USER" >&3 if ! grep "^$USER:" /etc/subgid >/dev/null 2>&3 || ! grep "^$USER:" /etc/subuid >/dev/null 2>&3; then echo "$base_toolbox_command: /etc/subgid and /etc/subuid don't have entries for user $USER" >&2 echo "See the podman(1), subgid(5), subuid(5) and usermod(8) manuals for more" >&2 echo "information." >&2 exit 1 fi fi if [ "$TOOLBOX_PATH" = "" ] 2>&3; then TOOLBOX_PATH="$toolbox_command_path" fi fi echo "$base_toolbox_command: TOOLBOX_PATH is $TOOLBOX_PATH" >&3 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 if [ -f /run/.containerenv ] 2>&3; then case $op in create | enter | list | rm | rmi | run | help ) if ! [ -f /run/.toolboxenv ] 2>&3; then echo "$base_toolbox_command: this is not a toolbox container" >&2 exit 1 fi # shellcheck disable=SC2119 forward_to_host exit "$?" ;; init-container ) init_container_home_link=false init_container_media_link=false init_container_mnt_link=false init_container_monitor_host=false while has_prefix "$1" -; do case $1 in -h | --help ) # shellcheck disable=SC2119 forward_to_host exit ;; --home ) shift exit_if_missing_argument --home "$1" init_container_home="$1" ;; --home-link ) init_container_home_link=true ;; --media-link ) init_container_media_link=true ;; --mnt-link ) init_container_mnt_link=true ;; --monitor-host ) init_container_monitor_host=true ;; --shell ) shift exit_if_missing_argument --shell "$1" init_container_shell="$1" ;; --uid ) shift exit_if_missing_argument --uid "$1" init_container_uid="$1" ;; --user ) shift exit_if_missing_argument --user "$1" init_container_user="$1" ;; * ) exit_if_unrecognized_option "$1" esac shift done init_container \ "$init_container_home" \ "$init_container_home_link" \ "$init_container_media_link" \ "$init_container_mnt_link" \ "$init_container_monitor_host" \ "$init_container_shell" \ "$init_container_uid" \ "$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 exit 1 ;; esac fi if ! cgroups_version=$(get_cgroups_version); then exit 1 fi echo "$base_toolbox_command: running on a cgroups v$cgroups_version host" >&3 if [ "$op" != "reset" ] 2>&3; then if ! migrate; then exit 1 fi fi case $op in create ) while has_prefix "$1" -; do case $1 in --candidate-registry ) registry=$registry_candidate ;; -c | --container ) shift exit_if_missing_argument --container "$1" arg=$1 if ! container_name_is_valid "$arg"; then echo "$base_toolbox_command: invalid argument for '--container'" >&2 echo "Container names must match '$container_name_regexp'." >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 exit 1 fi toolbox_container="$arg" ;; -h | --help ) help "$op" exit ;; -i | --image ) shift exit_if_missing_argument --image "$1" base_toolbox_image=$1 ;; -r | --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" if ! update_container_and_image_names; then exit 1 fi if ! create false; then exit 1 fi exit ;; enter ) while has_prefix "$1" -; do case $1 in -c | --container ) shift exit_if_missing_argument --container "$1" toolbox_container=$1 ;; -h | --help ) help "$op" exit ;; -r | --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" if ! update_container_and_image_names; then exit 1 fi enter exit ;; help ) while has_prefix "$1" -; do case $1 in -h | --help ) help "$op" exit ;; * ) exit_if_unrecognized_option "$1" esac # shellcheck disable=SC2317 shift done help "$1" exit ;; init-container ) while has_prefix "$1" -; do case $1 in -h | --help ) help "$op" exit esac shift done echo "$base_toolbox_command: The 'init-container' command can only be used inside containers" >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 exit 1 ;; list ) ls_add_empty_line=false ls_images=false ls_containers=false while has_prefix "$1" -; do case $1 in -c | --containers ) ls_containers=true ;; -h | --help ) help "$op" exit ;; -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 if $ls_images && [ "$images" != "" ] 2>&3; then echo "$images" ls_add_empty_line=true fi if $ls_containers && [ "$containers" != "" ] 2>&3; then $ls_add_empty_line && echo "" echo "$containers" fi exit ;; reset ) while has_prefix "$1" -; do case $1 in -h | --help ) help "$op" exit ;; * ) exit_if_unrecognized_option "$1" esac # shellcheck disable=SC2317 shift done exit_if_extra_operand "$1" reset exit "$?" ;; rm | rmi ) rm_all=false rm_force=false while has_prefix "$1" -; do case $1 in -a | --all ) rm_all=true ;; -f | --force ) rm_force=true ;; -h | --help ) help "$op" exit ;; * ) 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 exit ;; run ) while has_prefix "$1" -; do case $1 in -c | --container ) shift exit_if_missing_argument --container "$1" toolbox_container=$1 ;; -h | --help ) help "$op" exit ;; -r | --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_missing_argument "$op" "$1" if ! update_container_and_image_names; then exit 1 fi run false false true "$@" exit ;; * ) echo "$base_toolbox_command: unrecognized command '$op'" >&2 echo "Try '$base_toolbox_command --help' for more information." >&2 exit 1 esac