2021-11-18 16:53:42 +01:00
|
|
|
#!/bin/sh
|
|
|
|
|
# POSIX
|
2021-11-22 13:21:33 +01:00
|
|
|
# Expected env variables:
|
|
|
|
|
# HOME
|
|
|
|
|
# USER
|
2021-11-18 16:53:42 +01:00
|
|
|
|
2021-12-10 09:08:06 +01:00
|
|
|
trap '[ "$?" -ne 0 ] && printf "An error occurred\n"' EXIT
|
2021-11-21 15:57:57 +01:00
|
|
|
|
2021-11-22 03:13:52 +01:00
|
|
|
# We depend on podman let's be sure we have it
|
|
|
|
|
if ! command -v podman >/dev/null; then
|
2021-12-10 09:08:06 +01:00
|
|
|
printf >&2 "Missing dependency: podman\n"
|
2021-11-22 03:13:52 +01:00
|
|
|
exit 127
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Defaults
|
|
|
|
|
container_image="registry.fedoraproject.org/fedora-toolbox:35"
|
|
|
|
|
container_name="fedora-toolbox-35"
|
|
|
|
|
container_user_gid="$(id -rg)"
|
2021-12-10 23:46:29 +01:00
|
|
|
container_user_home="${HOME:-"/"}"
|
2021-11-18 16:53:42 +01:00
|
|
|
container_user_name="${USER}"
|
|
|
|
|
container_user_uid="$(id -ru)"
|
2021-12-01 19:29:17 +01:00
|
|
|
distrobox_entrypoint_path="$(command -v distrobox-init)"
|
|
|
|
|
distrobox_export_path="$(command -v distrobox-export)"
|
2021-11-18 16:53:42 +01:00
|
|
|
verbose=0
|
2021-12-08 16:31:36 +01:00
|
|
|
version="distrobox_version_placeholder"
|
2021-11-18 16:53:42 +01:00
|
|
|
|
|
|
|
|
# Print usage to stdout.
|
|
|
|
|
# Arguments:
|
|
|
|
|
# None
|
|
|
|
|
# Outputs:
|
|
|
|
|
# print usage with examples.
|
|
|
|
|
show_help() {
|
2021-12-10 09:08:06 +01:00
|
|
|
cat <<EOF
|
|
|
|
|
distrobox version: ${version}
|
2021-12-08 17:15:30 +01:00
|
|
|
|
2021-12-10 09:28:16 +01:00
|
|
|
distrobox-create takes care of creating the container with input name and image.
|
|
|
|
|
Created container will be tightly integrated with the host, allowing to share
|
2021-12-10 19:44:30 +01:00
|
|
|
the HOME directory of the user, external storage, external usb devices and
|
2021-12-10 09:28:16 +01:00
|
|
|
graphical apps (X11/Wayland) and audio.
|
|
|
|
|
|
2021-12-10 09:08:06 +01:00
|
|
|
Usage:
|
2021-12-01 19:29:17 +01:00
|
|
|
distrobox-create --image registry.fedoraproject.org/fedora-toolbox:35 --name fedora-toolbox-35
|
2021-11-18 16:53:42 +01:00
|
|
|
|
2021-12-10 09:08:06 +01:00
|
|
|
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
|
2021-11-18 16:53:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Parse arguments
|
|
|
|
|
while :; do
|
|
|
|
|
case $1 in
|
|
|
|
|
-h | --help)
|
|
|
|
|
# Call a "show_help" function to display a synopsis, then exit.
|
|
|
|
|
show_help
|
2021-12-09 18:28:39 +01:00
|
|
|
exit 0
|
2021-11-18 16:53:42 +01:00
|
|
|
;;
|
2021-12-02 19:54:01 +01:00
|
|
|
-v | --verbose)
|
2021-11-18 16:53:42 +01:00
|
|
|
verbose=1
|
|
|
|
|
shift
|
|
|
|
|
;;
|
2021-12-08 16:31:36 +01:00
|
|
|
-V | --version)
|
2021-12-10 09:08:06 +01:00
|
|
|
printf "distrobox: %s\n" "${version}"
|
2021-12-09 18:28:39 +01:00
|
|
|
exit 0
|
2021-12-08 16:31:36 +01:00
|
|
|
;;
|
2021-11-18 16:53:42 +01:00
|
|
|
-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
|
2021-11-21 15:05:32 +01:00
|
|
|
if [ "${verbose}" -ne 0 ]; then
|
2021-11-18 16:53:42 +01:00
|
|
|
set -o xtrace
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Generate Podman command to execute.
|
|
|
|
|
# Arguments:
|
|
|
|
|
# None
|
|
|
|
|
# Outputs:
|
2021-12-01 19:29:17 +01:00
|
|
|
# prints the podman command to create the distrobox container
|
2021-11-18 16:53:42 +01:00
|
|
|
generate_command() {
|
|
|
|
|
# Set the container hostname the same as the container name.
|
|
|
|
|
# use the host's namespace for ipc, network, pid, ulimit
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="podman create"
|
2021-12-10 09:28:16 +01:00
|
|
|
# add podman verbose if -v is specified
|
2021-12-02 19:54:01 +01:00
|
|
|
if [ "${verbose}" -ne 0 ]; then
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="${result_command} --log-level debug"
|
2021-12-02 19:54:01 +01:00
|
|
|
fi
|
2021-12-10 14:20:55 +01:00
|
|
|
result_command="${result_command}
|
2021-12-06 08:43:55 +01:00
|
|
|
--env=\"XDG_RUNTIME_DIR=/run/user/${container_user_uid}\"
|
2021-11-18 16:53:42 +01:00
|
|
|
--hostname ${container_name}
|
|
|
|
|
--ipc host"
|
|
|
|
|
# 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
|
|
|
|
|
#
|
2021-12-01 19:29:17 +01:00
|
|
|
# also mount the distrobox-init utility as the container entrypoint
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="${result_command} --name ${container_name}
|
2021-12-06 08:43:55 +01:00
|
|
|
--env=\"SHELL=${SHELL}\"
|
2021-11-18 16:53:42 +01:00
|
|
|
--network host
|
|
|
|
|
--pid host
|
|
|
|
|
--privileged
|
|
|
|
|
--security-opt label=disable
|
|
|
|
|
--ulimit host
|
|
|
|
|
--user root:root
|
|
|
|
|
--userns keep-id
|
2021-12-01 19:29:17 +01:00
|
|
|
--volume ${distrobox_entrypoint_path}:/usr/bin/entrypoint:ro"
|
2021-12-02 19:54:01 +01:00
|
|
|
|
2021-12-01 19:29:17 +01:00
|
|
|
# let's check if we can include distrobox-export or not
|
|
|
|
|
if [ -n "${distrobox_export_path}" ]; then
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="${result_command} --volume ${distrobox_export_path}:/usr/bin/distrobox-export:ro"
|
2021-11-22 03:13:52 +01:00
|
|
|
fi
|
2021-12-02 19:54:01 +01:00
|
|
|
|
2021-12-11 00:46:57 +01:00
|
|
|
result_command="${result_command} --volume ${container_user_home}:${container_user_home}:rslave"
|
|
|
|
|
result_command="${result_command} --volume /dev:/dev:rslave"
|
2021-12-02 19:54:01 +01:00
|
|
|
|
2021-12-08 17:04:27 +01:00
|
|
|
# useful mounts from host to the container using /run/host as a base
|
2021-12-09 18:43:18 +01:00
|
|
|
host_directories="/ /etc /media /mnt /run /tmp /usr /var"
|
2021-12-09 02:07:39 +01:00
|
|
|
for host_directory in ${host_directories}; do
|
|
|
|
|
# Check if the directory exists first
|
|
|
|
|
if [ -d "${host_directory}" ]; then
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="${result_command} --volume ${host_directory}:/run/host${host_directory}:rslave"
|
2021-12-02 19:54:01 +01:00
|
|
|
fi
|
|
|
|
|
done
|
2021-12-10 09:28:16 +01:00
|
|
|
|
2021-12-08 17:04:27 +01:00
|
|
|
# those are dynamic configs needed by the container to function properly
|
|
|
|
|
# and integrate with the host
|
2021-12-10 09:28:16 +01:00
|
|
|
host_links="/etc/host.conf /etc/hosts /etc/resolv.conf /etc/localtime"
|
2021-12-09 18:43:18 +01:00
|
|
|
for host_link in ${host_links}; do
|
2021-12-08 17:04:27 +01:00
|
|
|
# Check if the file exists first
|
2021-12-09 18:43:18 +01:00
|
|
|
if [ -f "${host_link}" ]; then
|
2021-12-08 17:04:27 +01:00
|
|
|
# Use realpath to not have multi symlink mess
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="${result_command} --volume $(realpath "${host_link}"):${host_link}:ro"
|
2021-12-08 17:04:27 +01:00
|
|
|
fi
|
|
|
|
|
done
|
2021-12-02 19:54:01 +01:00
|
|
|
|
2021-11-18 16:53:42 +01:00
|
|
|
# mount also the XDG_RUNTIME_DIR to ensure functionality of the apps
|
2021-12-11 00:46:57 +01:00
|
|
|
if [ -d "/run/user/${container_user_uid}" ]; then
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="${result_command} --volume /run/user/${container_user_uid}:/run/user/${container_user_uid}"
|
2021-11-18 16:53:42 +01:00
|
|
|
fi
|
2021-12-11 00:09:10 +01:00
|
|
|
result_command="${result_command} --mount type=devpts,destination=/dev/pts"
|
2021-12-02 19:54:01 +01:00
|
|
|
|
2021-11-18 16:53:42 +01:00
|
|
|
# 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
|
2021-12-09 19:20:46 +01:00
|
|
|
host_sockets="$(find /run -iname "*socket" ! -path "/run/user/*" 2>/dev/null || :)"
|
2021-12-05 16:53:34 +01:00
|
|
|
for socket in ${host_sockets}; do
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="${result_command} --volume ${socket}:${socket}"
|
2021-11-18 16:53:42 +01:00
|
|
|
done
|
2021-12-02 19:54:01 +01:00
|
|
|
|
2021-12-01 19:29:17 +01:00
|
|
|
# now execute the entrypoint, refer to `distrobox-init -h` for instructions
|
2021-12-10 09:08:06 +01:00
|
|
|
result_command="${result_command} ${container_image}
|
2021-12-11 00:46:57 +01:00
|
|
|
/usr/bin/entrypoint -v --name ${container_user_name}
|
|
|
|
|
--user ${container_user_uid}
|
|
|
|
|
--group ${container_user_gid}
|
|
|
|
|
--home ${container_user_home}"
|
2021-12-10 09:08:06 +01:00
|
|
|
|
|
|
|
|
printf "%s" "${result_command}"
|
2021-11-18 16:53:42 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-02 19:54:01 +01:00
|
|
|
# check that we have a complete distrobox installation or
|
|
|
|
|
# entrypoint and export will not work.
|
2021-12-10 09:08:06 +01:00
|
|
|
[ -z "${distrobox_entrypoint_path}" ] && printf >&2 "Error: no distrobox-init found in %s\n" "${PATH}" && exit 127
|
2021-12-02 19:54:01 +01:00
|
|
|
|
2021-11-18 16:53:42 +01:00
|
|
|
# First, check if the image exists in the host
|
2021-12-10 13:13:03 +01:00
|
|
|
if [ -z "$(podman images -q "${container_image}")" ]; then
|
2021-12-10 09:08:06 +01:00
|
|
|
printf >&2 "Image not found.\n"
|
2021-12-09 18:28:39 +01:00
|
|
|
printf >&2 "Do you want to pull the image now?[y/n] "
|
2021-12-09 18:43:18 +01:00
|
|
|
read -r response
|
|
|
|
|
# Accept only y,Y,Yes,yes,n,N,No,no.
|
|
|
|
|
case "${response}" in
|
2021-12-09 18:28:39 +01:00
|
|
|
y | Y | yes | Yes)
|
2021-12-09 18:43:18 +01:00
|
|
|
# Pull the image
|
2021-12-09 18:19:13 +01:00
|
|
|
podman pull "${container_image}"
|
|
|
|
|
;;
|
2021-12-09 18:28:39 +01:00
|
|
|
n | N | no | No)
|
2021-12-10 09:08:06 +01:00
|
|
|
printf >&2 "next time, run this command first:\n"
|
|
|
|
|
printf >&2 " podman pull %s\n" "${container_image}"
|
2021-12-09 18:28:39 +01:00
|
|
|
exit 0
|
2021-12-09 18:19:13 +01:00
|
|
|
;;
|
|
|
|
|
*) # Default case: If no more options then break out of the loop.
|
2021-12-10 09:08:06 +01:00
|
|
|
printf >&2 "The available choices are: y,Y,Yes,yes,n,N,No,no. Exiting.\n"
|
2021-12-09 18:28:39 +01:00
|
|
|
exit 1
|
|
|
|
|
;;
|
2021-12-09 18:19:13 +01:00
|
|
|
esac
|
2021-11-18 16:53:42 +01:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Generate the command and execute
|
2021-12-10 09:28:16 +01:00
|
|
|
cmd="$(generate_command)"
|
|
|
|
|
# shellcheck disable=SC2086
|
|
|
|
|
eval ${cmd}
|