539 lines
16 KiB
Bash
Executable file
539 lines
16 KiB
Bash
Executable file
#!/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.
|
||
#
|
||
|
||
|
||
source /etc/os-release
|
||
release=$VERSION_ID
|
||
|
||
prefix_sudo=""
|
||
registry="registry.fedoraproject.org"
|
||
registry_candidate="candidate-registry.fedoraproject.org"
|
||
spinner_animation="[>----] [=>---] [==>--] [===>-] [====>] [----<] [---<=] [--<==] [-<===] [<====]"
|
||
toolbox_prompt="🔹[\u@\h \W]\\$ "
|
||
verbose=false
|
||
|
||
|
||
is_integer()
|
||
{
|
||
[ "$1" != "" ] && [ $1 -eq $1 2>&42 ]
|
||
return $?
|
||
}
|
||
|
||
|
||
spinner_start()
|
||
(
|
||
directory="$1"
|
||
message="$2"
|
||
|
||
if $verbose; then
|
||
rm --force --recursive "$directory" 2>&42
|
||
return 0
|
||
fi
|
||
|
||
if ! touch "$directory/spinner-start" 2>&42; then
|
||
echo "$0: unable to start spinner: spinner start file couldn't be created"
|
||
return 1
|
||
fi
|
||
|
||
echo -n "$message"
|
||
tput civis 2>&42
|
||
|
||
(
|
||
while [ -f "$directory/spinner-start" ]; do
|
||
echo "$spinner_animation" | tr " " "\n" 2>&42 | while read frame; do
|
||
echo -n "$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>&42
|
||
|
||
if ! touch "$directory/spinner-stop" 2>&42; then
|
||
echo "$0: unable to stop spinner: spinner stop file couldn't be created"
|
||
fi
|
||
) &
|
||
|
||
return 0
|
||
)
|
||
|
||
|
||
spinner_stop()
|
||
(
|
||
$verbose && return
|
||
directory="$1"
|
||
|
||
if ! rm "$directory/spinner-start" 2>&42; then
|
||
echo "$0: unable to stop spinner: spinner start file couldn't be removed"
|
||
return
|
||
fi
|
||
|
||
while ! [ -f "$directory/spinner-stop" ]; do
|
||
:
|
||
done
|
||
|
||
rm --force --recursive "$directory" 2>&42
|
||
)
|
||
|
||
|
||
configure_working_container()
|
||
(
|
||
working_container_name="$1"
|
||
|
||
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>&42; then
|
||
$prefix_sudo buildah rm $working_container_name >/dev/null 2>&42
|
||
echo "$0: failed to make /home a symlink"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah run $working_container_name -- useradd \
|
||
--home-dir $HOME \
|
||
--no-create-home \
|
||
--shell $SHELL \
|
||
--uid $UID \
|
||
--groups wheel \
|
||
$USER \
|
||
>/dev/null 2>&42; then
|
||
echo "$0: failed to create user $USER with UID $UID"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah run $working_container_name -- passwd -d $USER >/dev/null 2>&42; then
|
||
echo "$0: failed to remove password for user $USER"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah run $working_container_name -- passwd -d root >/dev/null 2>&42; then
|
||
echo "$0: failed to remove password for user root"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah config --volume $HOME $working_container_name >/dev/null 2>&42; then
|
||
echo "$0: failed to configure volume for $HOME"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah config --volume $XDG_RUNTIME_DIR $working_container_name >/dev/null 2>&42; then
|
||
echo "$0: failed to configure volume for $XDG_RUNTIME_DIR"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah config --volume $dbus_system_bus_path $working_container_name >/dev/null 2>&42; then
|
||
echo "$0: failed to configure volume for $dbus_system_bus_path"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah config --volume /dev/dri $working_container_name >/dev/null 2>&42; then
|
||
echo "$0: failed to configure volume for /dev/dri"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah config --volume /dev/fuse $working_container_name >/dev/null 2>&42; then
|
||
echo "$0: failed to configure volume for /dev/fuse"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah config --user $USER $working_container_name >/dev/null 2>&42; then
|
||
echo "$0: failed to configure the default user as $USER"
|
||
return 1
|
||
fi
|
||
|
||
if ! $prefix_sudo buildah config --workingdir $HOME $working_container_name >/dev/null 2>&42; then
|
||
echo "$0: failed to configure the initial working directory to $HOME"
|
||
return 1
|
||
fi
|
||
|
||
return 0
|
||
)
|
||
|
||
|
||
create()
|
||
(
|
||
dbus_system_bus_address="unix:path=/var/run/dbus/system_bus_socket"
|
||
tmpfs_size=$((64 * 1024 * 1024)) # 64 MiB
|
||
working_container_name="fedora-toolbox-working-container-$(uuidgen --time)"
|
||
|
||
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>&42)
|
||
dbus_system_bus_path=$(readlink --canonicalize $dbus_system_bus_path 2>&42)
|
||
|
||
echo "$0: checking if image $toolbox_image already exists" >&42
|
||
|
||
if ! $prefix_sudo buildah inspect --type image $toolbox_image >/dev/null 2>&42; then
|
||
echo "$0: trying to create working container $working_container_name" >&42
|
||
echo "$0: looking for image localhost/$base_toolbox_image" >&42
|
||
|
||
if ! $prefix_sudo buildah from \
|
||
--name $working_container_name \
|
||
localhost/$base_toolbox_image >/dev/null 2>&42; then
|
||
echo "$0: looking for image $registry/$fgc/$base_toolbox_image" >&42
|
||
|
||
if spinner_directory=$(mktemp --directory --tmpdir fedora-toolbox-spinner-XXXXXXXXXX 2>&42); then
|
||
spinner_message="$0: pulling from $registry: "
|
||
if ! spinner_start "$spinner_directory" "$spinner_message"; then
|
||
spinner_directory=""
|
||
fi
|
||
else
|
||
echo "$0: unable to start spinner: spinner directory not created"
|
||
spinner_directory=""
|
||
fi
|
||
|
||
$prefix_sudo buildah from \
|
||
--name $working_container_name \
|
||
$registry/$fgc/$base_toolbox_image >/dev/null 2>&42
|
||
ret_val=$?
|
||
|
||
if [ "$spinner_directory" != "" ]; then
|
||
spinner_stop "$spinner_directory"
|
||
fi
|
||
|
||
if [ $ret_val -ne 0 ]; then
|
||
echo "$0: failed to create working container"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
echo "$0: trying to configure working container $working_container_name" >&42
|
||
|
||
if spinner_directory=$(mktemp --directory --tmpdir fedora-toolbox-spinner-XXXXXXXXXX 2>&42); then
|
||
spinner_message="$0: configuring working container: "
|
||
if ! spinner_start "$spinner_directory" "$spinner_message"; then
|
||
spinner_directory=""
|
||
fi
|
||
else
|
||
echo "$0: unable to start spinner: spinner directory not created"
|
||
spinner_directory=""
|
||
fi
|
||
|
||
configure_working_container $working_container_name
|
||
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>&42
|
||
exit 1
|
||
fi
|
||
|
||
echo "$0: trying to create image $toolbox_image" >&42
|
||
|
||
if spinner_directory=$(mktemp --directory --tmpdir fedora-toolbox-spinner-XXXXXXXXXX 2>&42); then
|
||
spinner_message="$0: creating image $toolbox_image: "
|
||
if ! spinner_start "$spinner_directory" "$spinner_message"; then
|
||
spinner_directory=""
|
||
fi
|
||
else
|
||
echo "$0: unable to start spinner: spinner directory not created"
|
||
spinner_directory=""
|
||
fi
|
||
|
||
$prefix_sudo buildah commit --rm $working_container_name $toolbox_image >/dev/null 2>&42
|
||
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>&42
|
||
echo "$0: failed to create image $toolbox_image"
|
||
exit 1
|
||
fi
|
||
|
||
echo "$0: created image $toolbox_image" >&42
|
||
fi
|
||
|
||
echo "$0: checking if container $toolbox_container already exists" >&42
|
||
|
||
if $prefix_sudo podman inspect --type container $toolbox_container >/dev/null 2>&42; then
|
||
echo "$0: container $toolbox_container already exists"
|
||
exit 1
|
||
fi
|
||
|
||
total_ram=$(awk '( $1 == "MemTotal:" ) { print $2 }' /proc/meminfo 2>&42) # 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 - UID))
|
||
uid_plus_one=$((UID + 1))
|
||
|
||
echo "$0: trying to create container $toolbox_container" >&42
|
||
|
||
if spinner_directory=$(mktemp --directory --tmpdir fedora-toolbox-spinner-XXXXXXXXXX 2>&42); then
|
||
spinner_message="$0: creating container $toolbox_container: "
|
||
if ! spinner_start "$spinner_directory" "$spinner_message"; then
|
||
spinner_directory=""
|
||
fi
|
||
else
|
||
echo "$0: unable to start spinner: spinner directory not created"
|
||
spinner_directory=""
|
||
fi
|
||
|
||
$prefix_sudo podman create \
|
||
--group-add wheel \
|
||
--hostname toolbox \
|
||
--interactive \
|
||
--name $toolbox_container \
|
||
--network host \
|
||
--privileged \
|
||
--security-opt label=disable \
|
||
--tmpfs /dev/shm:size=$tmpfs_size \
|
||
--tty \
|
||
--uidmap $UID:0:1 \
|
||
--uidmap 0:1:$UID \
|
||
--uidmap $uid_plus_one:$uid_plus_one:$max_minus_uid \
|
||
--volume $HOME:$HOME:rslave \
|
||
--volume $XDG_RUNTIME_DIR:$XDG_RUNTIME_DIR \
|
||
--volume $dbus_system_bus_path:$dbus_system_bus_path \
|
||
--volume /dev/dri:/dev/dri \
|
||
--volume /dev/fuse:/dev/fuse \
|
||
$toolbox_image \
|
||
/bin/sh >/dev/null 2>&42
|
||
ret_val=$?
|
||
|
||
if [ "$spinner_directory" != "" ]; then
|
||
spinner_stop "$spinner_directory"
|
||
fi
|
||
|
||
if [ $ret_val -ne 0 ]; then
|
||
echo "$0: failed to create container $toolbox_container"
|
||
exit 1
|
||
fi
|
||
|
||
echo "$0: created container $toolbox_container" >&42
|
||
)
|
||
|
||
|
||
enter()
|
||
(
|
||
shell_to_exec=/bin/bash
|
||
|
||
echo "$0: trying to start container $toolbox_container" >&42
|
||
|
||
if ! $prefix_sudo podman start $toolbox_container >/dev/null 2>&42; then
|
||
echo "$0: failed to start container $toolbox_container"
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$DBUS_SYSTEM_BUS_ADDRESS" != "" ]; then
|
||
set_dbus_system_bus_address="--env DBUS_SYSTEM_BUS_ADDRESS=$DBUS_SYSTEM_BUS_ADDRESS"
|
||
fi
|
||
|
||
echo "$0: looking for $SHELL in container $toolbox_container" >&42
|
||
|
||
if $prefix_sudo podman exec $toolbox_container test -f $SHELL 2>&42; then
|
||
shell_to_exec=$SHELL
|
||
else
|
||
echo "$0: $SHELL not found in $toolbox_container; using $shell_to_exec instead" >&42
|
||
fi
|
||
|
||
echo "$0: trying to exec $shell_to_exec in container $toolbox_container" >&42
|
||
|
||
$prefix_sudo podman exec \
|
||
--env COLORTERM=$COLORTERM \
|
||
--env DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS \
|
||
$set_dbus_system_bus_address \
|
||
--env DESKTOP_SESSION=$DESKTOP_SESSION \
|
||
--env DISPLAY=$DISPLAY \
|
||
--env LANG=$LANG \
|
||
--env SHELL=$SHELL \
|
||
--env SSH_AUTH_SOCK=$SSH_AUTH_SOCK \
|
||
--env TERM=$TERM \
|
||
--env VTE_VERSION=$VTE_VERSION \
|
||
--env XDG_CURRENT_DESKTOP=$XDG_CURRENT_DESKTOP \
|
||
--env XDG_DATA_DIRS=$XDG_DATA_DIRS \
|
||
--env XDG_MENU_PREFIX=$XDG_MENU_PREFIX \
|
||
--env XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR \
|
||
--env XDG_SEAT=$XDG_SEAT \
|
||
--env XDG_SESSION_DESKTOP=$XDG_SESSION_DESKTOP \
|
||
--env XDG_SESSION_ID=$XDG_SESSION_ID \
|
||
--env XDG_SESSION_TYPE=$XDG_SESSION_TYPE \
|
||
--env XDG_VTNR=$XDG_VTNR \
|
||
--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>&42
|
||
)
|
||
|
||
|
||
exit_if_extra_operand()
|
||
{
|
||
if [ "$1" != "" ]; then
|
||
echo "$0: extra operand '$1'"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
|
||
exit_if_unrecognized_option()
|
||
{
|
||
echo "$0: unrecognized option '$1'"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
}
|
||
|
||
|
||
usage()
|
||
{
|
||
echo "Usage: fedora-toolbox [--container <name>]"
|
||
echo " [--release <release>]"
|
||
echo " [-v | --verbose]"
|
||
echo " create [--candidate-registry]"
|
||
echo " [--image <name>]"
|
||
echo " or: fedora-toolbox [--container <name>]"
|
||
echo " [--release <release>]"
|
||
echo " [-v | --verbose]"
|
||
echo " enter"
|
||
echo " or: fedora-toolbox --help"
|
||
}
|
||
|
||
|
||
exec 42>/dev/null
|
||
|
||
while [[ "$1" = -* ]]; do
|
||
case $1 in
|
||
--container )
|
||
shift
|
||
if [ "$1" = "" ]; then
|
||
echo "$0: missing argument for '--container'"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
fi
|
||
toolbox_container=$1
|
||
;;
|
||
-h | --help )
|
||
usage
|
||
exit
|
||
;;
|
||
--release )
|
||
shift
|
||
if [ "$1" = "" ]; then
|
||
echo "$0: missing argument for '--release'"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
fi
|
||
arg=$(echo $1 | sed 's/^F\|^f//' 2>&42)
|
||
if ! is_integer $arg; then
|
||
echo "$0: invalid argument for '--release'"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
fi
|
||
if [ $arg -le 0 2>&42 ]; then
|
||
echo "$0: invalid argument for '--release'"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
fi
|
||
release=$arg
|
||
;;
|
||
--sudo )
|
||
prefix_sudo="sudo"
|
||
;;
|
||
-v | --verbose )
|
||
exec 42>&2
|
||
verbose=true
|
||
;;
|
||
* )
|
||
exit_if_unrecognized_option $1
|
||
esac
|
||
shift
|
||
done
|
||
|
||
fgc="f$release"
|
||
[ "$toolbox_container" = "" ] && toolbox_container="fedora-toolbox-$USER:$release"
|
||
base_toolbox_image="fedora-toolbox:$release"
|
||
toolbox_image="fedora-toolbox-$USER:$release"
|
||
|
||
if [ "$1" = "" ]; then
|
||
echo "$0: missing command"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
fi
|
||
|
||
op=$1
|
||
shift
|
||
|
||
case $op in
|
||
create )
|
||
while [[ "$1" = -* ]]; do
|
||
case $1 in
|
||
--candidate-registry )
|
||
registry=$registry_candidate
|
||
;;
|
||
--image )
|
||
shift
|
||
if [ "$1" = "" ]; then
|
||
echo "$0: missing argument for '--image'"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
fi
|
||
toolbox_image=$1
|
||
;;
|
||
* )
|
||
exit_if_unrecognized_option $1
|
||
esac
|
||
shift
|
||
done
|
||
exit_if_extra_operand $1
|
||
create
|
||
exit
|
||
;;
|
||
enter )
|
||
while [[ "$1" = -* ]]; do
|
||
case $1 in
|
||
* )
|
||
exit_if_unrecognized_option $1
|
||
esac
|
||
shift
|
||
done
|
||
exit_if_extra_operand $1
|
||
enter
|
||
exit
|
||
;;
|
||
* )
|
||
echo "$0: unrecognized command '$op'"
|
||
echo "Try '$0 --help' for more information."
|
||
exit 1
|
||
esac
|