mirror of
https://github.com/89luca89/distrobox.git
synced 2026-08-19 01:14:49 -05:00
263 lines
7.7 KiB
Bash
Executable File
263 lines
7.7 KiB
Bash
Executable File
#!/bin/sh
|
|
# POSIX
|
|
# Expected env variables:
|
|
# HOME
|
|
# USER
|
|
|
|
trap '[ "$?" -ne 0 ] && printf "\nAn error occurred\n"' EXIT
|
|
|
|
# Defaults
|
|
container_image="registry.fedoraproject.org/fedora-toolbox:35"
|
|
container_name="fedora-toolbox-35"
|
|
container_user_gid="$(id -rg)"
|
|
container_user_home="${HOME:-"/"}"
|
|
container_user_name="${USER}"
|
|
container_user_uid="$(id -ru)"
|
|
distrobox_entrypoint_path="$(command -v distrobox-init)"
|
|
distrobox_export_path="$(command -v distrobox-export)"
|
|
verbose=0
|
|
version="1.2.2"
|
|
|
|
# Print usage to stdout.
|
|
# Arguments:
|
|
# None
|
|
# Outputs:
|
|
# print usage with examples.
|
|
show_help() {
|
|
cat <<EOF
|
|
|
|
distrobox version: ${version}
|
|
|
|
distrobox-create takes care of creating the container with input name and image.
|
|
The created container will be tightly integrated with the host, allowing sharing of
|
|
the HOME directory of the user, external storage, external usb devices and
|
|
graphical apps (X11/Wayland), and audio.
|
|
|
|
Usage:
|
|
|
|
distrobox-create --image registry.fedoraproject.org/fedora-toolbox:35 --name fedora-toolbox-35
|
|
|
|
Options:
|
|
|
|
--image/-i: image to use for the container default: registry.fedoraproject.org/fedora-toolbox:35
|
|
--name/-n: name for the distrobox default: fedora-toolbox-35
|
|
--help/-h: show this message
|
|
--verbose/-v: show more verbosity
|
|
--version/-V: show version
|
|
EOF
|
|
}
|
|
|
|
# Parse arguments
|
|
while :; do
|
|
case $1 in
|
|
-h | --help)
|
|
# Call a "show_help" function to display a synopsis, then exit.
|
|
show_help
|
|
exit 0
|
|
;;
|
|
-v | --verbose)
|
|
verbose=1
|
|
shift
|
|
;;
|
|
-V | --version)
|
|
printf "distrobox: %s\n" "${version}"
|
|
exit 0
|
|
;;
|
|
-i | --image)
|
|
if [ -n "$2" ]; then
|
|
container_image="$2"
|
|
shift
|
|
shift
|
|
fi
|
|
;;
|
|
-n | --name)
|
|
if [ -n "$2" ]; then
|
|
container_name="$2"
|
|
shift
|
|
shift
|
|
fi
|
|
;;
|
|
--) # End of all options.
|
|
shift
|
|
break
|
|
;;
|
|
*) # Default case: If no more options then break out of the loop.
|
|
break ;;
|
|
esac
|
|
done
|
|
|
|
set -o errexit
|
|
set -o nounset
|
|
# set verbosity
|
|
if [ "${verbose}" -ne 0 ]; then
|
|
set -o xtrace
|
|
fi
|
|
|
|
# We depend on a container manager let's be sure we have it
|
|
# First we use podman, else docker
|
|
container_manager="podman"
|
|
# Be sure we have a container manager to work with.
|
|
if ! command -v podman >/dev/null; then
|
|
# If no podman, try docker.
|
|
container_manager="docker"
|
|
if ! command -v docker >/dev/null; then
|
|
# Error: we need at least one between docker or podman.
|
|
printf >&2 "Missing dependency: we need a container manager\n."
|
|
printf >&2 "Please install one of podman or docker.\n"
|
|
exit 127
|
|
fi
|
|
fi
|
|
# add verbose if -v is specified
|
|
if [ "${verbose}" -ne 0 ]; then
|
|
container_manager="${container_manager} --log-level debug"
|
|
fi
|
|
|
|
# Generate Podman or Docker command to execute.
|
|
# Arguments:
|
|
# None
|
|
# Outputs:
|
|
# prints the podman or docker command to create the distrobox container
|
|
generate_command() {
|
|
# Set the container hostname the same as the container name.
|
|
result_command="${container_manager} create"
|
|
# use the host's namespace for ipc, network, pid, ulimit
|
|
result_command="${result_command}
|
|
--hostname ${container_name}
|
|
--ipc host
|
|
--name ${container_name}
|
|
--network host
|
|
--pid host
|
|
--privileged
|
|
--security-opt label=disable
|
|
--user root:root"
|
|
|
|
# let's check if we can include distrobox-export or not
|
|
if [ -n "${distrobox_export_path}" ]; then
|
|
result_command="${result_command}
|
|
--volume ${distrobox_export_path}:/usr/bin/distrobox-export:ro"
|
|
fi
|
|
|
|
# Mount useful stuff inside the container.
|
|
# We also mount host's root filesystem to /run/host, to be able to syphon
|
|
# dynamic configurations from the host.
|
|
#
|
|
# Mount user home, dev and host's root inside container.
|
|
# This grants access to external devices like usb webcams, disks and so on.
|
|
#
|
|
# Mount also the distrobox-init utility as the container entrypoint.
|
|
result_command="${result_command}
|
|
--volume ${container_user_home}:${container_user_home}:rslave
|
|
--volume ${distrobox_entrypoint_path}:/usr/bin/entrypoint:ro
|
|
--volume /:/run/host:rslave
|
|
--volume /dev:/dev:rslave
|
|
--volume /sys:/sys:rslave
|
|
--volume /tmp:/tmp:rslave"
|
|
|
|
# Mount also the XDG_RUNTIME_DIR to ensure functionality of the apps.
|
|
if [ -d "/run/user/${container_user_uid}" ]; then
|
|
result_command="${result_command}
|
|
--volume /run/user/${container_user_uid}:/run/user/${container_user_uid}"
|
|
fi
|
|
|
|
# Find all the user's socket and mount them inside the container
|
|
# this will allow for continuity of functionality between host and container
|
|
#
|
|
# for example using `podman --remote` to control the host's podman from inside
|
|
# the container or accessing docker and libvirt sockets.
|
|
host_sockets="$(find /run -iname "*sock" ! -path "/run/user/*" 2>/dev/null || :)"
|
|
for host_socket in ${host_sockets}; do
|
|
if [ -r "${host_socket}" ]; then
|
|
result_command="${result_command}
|
|
--volume $(realpath "${host_socket}"):${host_socket}"
|
|
fi
|
|
done
|
|
|
|
# These are dynamic configs needed by the container to function properly
|
|
# and integrate with the host
|
|
#
|
|
# We're doing this now instead of inside the init because some distros will
|
|
# have symlinks places for these files that use absolute paths instead of
|
|
# relative paths. Those symlinks will result broken inside the container so
|
|
# we need to resolve them now on the host.
|
|
host_links="/etc/host.conf /etc/hosts /etc/resolv.conf /etc/localtime"
|
|
for host_link in ${host_links}; do
|
|
# Check if the file exists first
|
|
if [ -f "${host_link}" ] && [ -r "${host_link}" ]; then
|
|
# Use realpath to not have multi symlink mess
|
|
result_command="${result_command}
|
|
--volume $(realpath "${host_link}"):${host_link}:ro"
|
|
fi
|
|
done
|
|
|
|
# These flags are not supported by docker, so we use them only if our
|
|
# container manager is podman.
|
|
if [ "${container_manager}" = "podman" ]; then
|
|
result_command="${result_command}
|
|
--userns keep-id
|
|
--ulimit host
|
|
--mount type=devpts,destination=/dev/pts"
|
|
fi
|
|
# Now execute the entrypoint, refer to `distrobox-init -h` for instructions
|
|
result_command="${result_command} ${container_image}
|
|
/usr/bin/entrypoint -v --name ${container_user_name}
|
|
--user ${container_user_uid}
|
|
--group ${container_user_gid}
|
|
--home ${container_user_home}"
|
|
|
|
# Return generated command.
|
|
printf "%s" "${result_command}"
|
|
}
|
|
|
|
# Check that we have a complete distrobox installation or
|
|
# entrypoint and export will not work.
|
|
if [ -z "${distrobox_entrypoint_path}" ]; then
|
|
printf >&2 "Error: no distrobox-init found in %s\n" "${PATH}"
|
|
exit 127
|
|
fi
|
|
|
|
# Check if the container already exists.
|
|
# If it does, notify the user and exit.
|
|
if ${container_manager} inspect --type container "${container_name}" >/dev/null 2>&1; then
|
|
printf "Distrobox named '%s' already exists.\n" "${container_name}"
|
|
printf "To enter, run:\n"
|
|
printf "\tdistrobox-enter --name %s\n" "${container_name}"
|
|
exit 0
|
|
fi
|
|
|
|
# First, check if the image exists in the host.
|
|
# If not prompt to download it.
|
|
if ! ${container_manager} inspect --type image "${container_image}" >/dev/null 2>&1; then
|
|
# Prompt to download it.
|
|
printf >&2 "Image not found.\n"
|
|
printf >&2 "Do you want to pull the image now?[y/n] "
|
|
read -r response
|
|
|
|
# Accept only y,Y,Yes,yes,n,N,No,no.
|
|
case "${response}" in
|
|
y | Y | yes | Yes)
|
|
# Pull the image
|
|
${container_manager} pull "${container_image}"
|
|
;;
|
|
n | N | no | No)
|
|
printf >&2 "next time, run this command first:\n"
|
|
printf >&2 "\t%s pull %s\n" "${container_manager}" "${container_image}"
|
|
exit 0
|
|
;;
|
|
*) # Default case: If no more options then break out of the loop.
|
|
printf >&2 "Invalid input.\n"
|
|
printf >&2 "The available choices are: y,Y,Yes,yes or n,N,No,no.\nExiting.\n"
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# Generate the create command and run it
|
|
cmd="$(generate_command)"
|
|
# Eval the generated command. If successful display an helpful message.
|
|
# shellcheck disable=SC2086
|
|
if eval ${cmd}; then
|
|
printf "Distrobox '%s' successfully created.\n" "${container_name}"
|
|
printf "To enter, run:\n"
|
|
printf "\tdistrobox-enter --name %s\n" "${container_name}"
|
|
fi
|