#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-only
#
# This file is part of the distrobox project:
#    https://github.com/89luca89/distrobox
#
# Copyright (C) 2021 distrobox contributors
#
# distrobox is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3
# as published by the Free Software Foundation.
#
# distrobox is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with distrobox; if not, see <http://www.gnu.org/licenses/>.

# POSIX
# Expected env variables:
#	HOME
#	USER
# Optional env variables:
#	DBX_CONTAINER_ALWAYS_PULL
#	DBX_CONTAINER_CUSTOM_HOME
#	DBX_CONTAINER_HOME_PREFIX
#	DBX_CONTAINER_IMAGE
#	DBX_CONTAINER_MANAGER
#	DBX_CONTAINER_NAME
#	DBX_CONTAINER_GENERATE_ENTRY
#	DBX_NON_INTERACTIVE
#	DBX_SUDO_PROGRAM

# Despite of running this script via SUDO/DOAS being not supported (the
# script itself will call the appropriate tool when necessary), we still want
# to allow people to run it as root, logged in in a shell, and create rootful
# containers.
#
# SUDO_USER is a variable set by SUDO and can be used to check whether the script was called by it. Same thing for DOAS_USER, set by DOAS.
if [ -n "${SUDO_USER}" ] || [ -n "${DOAS_USER}" ]; then
	printf >&2 "Running %s via SUDO/DOAS is not supported. Instead, please try running:\n" "$(basename "${0}")"
	printf >&2 "  %s --root %s\n" "$(basename "${0}")" "$*"
	exit 1
fi

# Defaults
container_additional_packages=""
container_always_pull=0
container_clone=""
container_generate_entry=1
container_home_prefix=""
container_image=""
container_image_default="registry.fedoraproject.org/fedora-toolbox:38"
container_init_hook=""
container_manager="autodetect"
container_manager_additional_flags=""
container_name=""
container_name_default="my-distrobox"
container_pre_init_hook=""
container_user_custom_home=""
container_user_gid="$(id -rg)"
container_user_home="${HOME:-"/"}"
container_user_name="${USER}"
container_user_uid="$(id -ru)"
dryrun=0
init=0
non_interactive=0
nvidia=0
unshare_ipc=0
unshare_netns=0
# Use cd + dirname + pwd so that we do not have relative paths in mount points
# We're not using "realpath" here so that symlinks are not resolved this way
# "realpath" would break situations like Nix or similar symlink based package
# management.
distrobox_entrypoint_path="$(cd "$(dirname "${0}")" && pwd)/distrobox-init"
distrobox_export_path="$(cd "$(dirname "${0}")" && pwd)/distrobox-export"
distrobox_genentry_path="$(cd "$(dirname "${0}")" && pwd)/distrobox-generate-entry"
distrobox_hostexec_path="$(cd "$(dirname "${0}")" && pwd)/distrobox-host-exec"
# In case some of the scripts are not in the same path as create, let's search
# in PATH for them.
[ ! -e "${distrobox_entrypoint_path}" ] && distrobox_entrypoint_path="$(command -v distrobox-init)"
[ ! -e "${distrobox_export_path}" ] && distrobox_export_path="$(command -v distrobox-export)"
[ ! -e "${distrobox_genentry_path}" ] && distrobox_genentry_path="$(command -v distrobox-generate-entry)"
[ ! -e "${distrobox_hostexec_path}" ] && distrobox_hostexec_path="$(command -v distrobox-host-exec)"
# If the user runs this script as root in a login shell, set rootful=1.
# There's no need for them to pass the --root flag option in such cases.
[ "${container_user_uid}" -eq 0 ] && rootful=1 || rootful=0
verbose=0
version="1.5.0.2"

# Source configuration files, this is done in an hierarchy so local files have
# priority over system defaults
# leave priority to environment variables.
config_files="
	/usr/share/distrobox/distrobox.conf
	/usr/etc/distrobox/distrobox.conf
	/etc/distrobox/distrobox.conf
	${XDG_CONFIG_HOME:-"${HOME}/.config"}/distrobox/distrobox.conf
	${HOME}/.distroboxrc
"
for config_file in ${config_files}; do
	# Shellcheck will give error for sourcing a variable file as it cannot follow
	# it. We don't care so let's disable this linting for now.
	# shellcheck disable=SC1090
	[ -e "${config_file}" ] && . "${config_file}"
done
# If we're running this script as root -- as in logged in in the shell as root
# user, and not via SUDO/DOAS --, we don't need to set distrobox_sudo_program
# as it's meaningless for this use case.
if [ "${container_user_uid}" -ne 0 ]; then
	# If the DBX_SUDO_PROGRAM/distrobox_sudo_program variable was set by the
	# user, use its value instead of "sudo". But only if not running the script
	# as root (UID 0).
	distrobox_sudo_program=${DBX_SUDO_PROGRAM:-${distrobox_sudo_program:-"sudo"}}
fi
# Fixup non_interactive=[true|false], in case we find it in the config file(s)
[ "${non_interactive}" = "true" ] && non_interactive=1
[ "${non_interactive}" = "false" ] && non_interactive=0

[ -n "${DBX_CONTAINER_ALWAYS_PULL}" ] && container_always_pull="${DBX_CONTAINER_ALWAYS_PULL}"
[ -n "${DBX_CONTAINER_CUSTOM_HOME}" ] && container_user_custom_home="${DBX_CONTAINER_CUSTOM_HOME}"
[ -n "${DBX_CONTAINER_HOME_PREFIX}" ] && container_home_prefix="${DBX_CONTAINER_HOME_PREFIX}"
[ -n "${DBX_CONTAINER_IMAGE}" ] && container_image="${DBX_CONTAINER_IMAGE}"
[ -n "${DBX_CONTAINER_MANAGER}" ] && container_manager="${DBX_CONTAINER_MANAGER}"
[ -n "${DBX_CONTAINER_NAME}" ] && container_name="${DBX_CONTAINER_NAME}"
[ -n "${DBX_NON_INTERACTIVE}" ] && non_interactive="${DBX_NON_INTERACTIVE}"
[ -n "${DBX_container_generate_entry}" ] && container_generate_entry="${DBX_container_generate_entry}"

# Print usage to stdout.
# Arguments:
#   None
# Outputs:
#   print usage with examples.
show_help() {
	cat << EOF
distrobox version: ${version}

Usage:

	distrobox create --image alpine:latest --name test --init-hooks "touch /var/tmp/test1 && touch /var/tmp/test2"
	distrobox create --image fedora:38 --name test --additional-flags "--env MY_VAR-value"
	distrobox create --image fedora:38 --name test --volume /opt/my-dir:/usr/local/my-dir:rw --additional-flags "--pids-limit -1"
	distrobox create -i docker.io/almalinux/8-init --init --name test --pre-init-hooks "dnf config-manager --enable powertools && dnf -y install epel-release"
	distrobox create --clone fedora-38 --name fedora-38-copy
	distrobox create --image alpine my-alpine-container
	distrobox create --image registry.fedoraproject.org/fedora-toolbox:38 --name fedora-toolbox-38
	distrobox create --pull --image centos:stream9 --home ~/distrobox/centos9
	distrobox create --image alpine:latest --name test2 --additional-packages "git tmux vim"
	distrobox create --image ubuntu:22.04 --name ubuntu-nvidia --nvidia

	DBX_NON_INTERACTIVE=1 DBX_CONTAINER_NAME=test-alpine DBX_CONTAINER_IMAGE=alpine distrobox-create

Options:

	--image/-i:		image to use for the container	default: ${container_image_default}
	--name/-n:		name for the distrobox		default: ${container_name_default}
	--pull/-p:		pull the image even if it exists locally (implies --yes)
	--yes/-Y:		non-interactive, pull images without asking
	--root/-r:		launch podman/docker with root privileges. Note that if you need root this is the preferred
				way over "sudo distrobox" (note: if using a program other than 'sudo' for root privileges is necessary,
				specify it through the DBX_SUDO_PROGRAM env variable, or 'distrobox_sudo_program' config variable)
	--clone/-c:		name of the distrobox container to use as base for a new container
				this will be useful to either rename an existing distrobox or have multiple copies
				of the same environment.
	--home/-H:		select a custom HOME directory for the container. Useful to avoid host's home littering with temp files.
	--volume:		additional volumes to add to the container
	--additional-flags/-a:	additional flags to pass to the container manager command
	--additional-packages/-ap:	additional packages to install during initial container setup
	--init-hooks:		additional commands to execute during container initialization
	--pre-init-hooks:	additional commands to execute prior to container initialization
	--init/-I:		use init system (like systemd) inside the container.
				this will make host's processes not visible from within the container.
	--nvidia:		try to integrate host's nVidia drivers in the guest
	--unshare-netns:        do not share the net namespace with host
	--unshare-ipc:          do not share ipc namemspace with host
	--compatibility/-C:	show list of compatible images
	--help/-h:		show this message
	--no-entry:		do not generate a container entry in the application list
	--dry-run/-d:		only print the container manager command generated
	--verbose/-v:		show more verbosity
	--version/-V:		show version

Compatibility:

	for a list of compatible images and container managers, please consult the man page:
		man distrobox-compatibility
	or run
		distrobox create --compatibility
	or consult the documentation page on: https://github.com/89luca89/distrobox/blob/main/docs/compatibility.md
EOF
}

# Print list of compatible images to stdou, caching locally in a file.
# Arguments:
#   None
# Outputs:
#   print usage with examples.
show_compatibility() {
	if [ ! -e "${HOME}/.cache/distrobox-compatibility-${version}" ] ||
		[ ! -s "${HOME}/.cache/distrobox-compatibility-${version}" ]; then
		mkdir -p "${HOME}/.cache"

		# If we don't have a cache file, we need connectivity. Ensure we have
		# one and return error if not.
		if ! curl -s "https://github.com" > /dev/null; then
			printf >&2 "ERROR: no cache file and no connectivity found, cannot retrieve compatibility list.\n"
			exit 1
		fi
		# We want to download the correspondent version of the compatibility table and extract a list from it.
		# Always use the docs as source of truth for this.
		curl -s \
			"https://raw.githubusercontent.com/89luca89/distrobox/${version}/docs/compatibility.md" |
			sed -n -e '/| Alma/,/| Void/ p' |
			cut -d '|' -f 4 |
			sed 's|<br>|\n|g' |
			tr -d ' ' |
			sort -u > "${HOME}/.cache/distrobox-compatibility-${version}"
	fi
	cat "${HOME}/.cache/distrobox-compatibility-${version}"
}

# 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
			;;
		--no-entry)
			shift
			container_generate_entry=0
			;;
		-d | --dry-run)
			shift
			dryrun=1
			;;
		-r | --root)
			shift
			rootful=1
			;;
		-I | --init)
			shift
			init=1
			;;
		--unshare-netns)
			shift
			unshare_netns=1
			;;
		--unshare-ipc)
			shift
			unshare_ipc=1
			;;
		-C | --compatibility)
			show_compatibility
			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
			;;
		-c | --clone)
			if [ -n "$2" ]; then
				container_clone="$2"
				shift
				shift
			fi
			;;
		-H | --home)
			if [ -n "$2" ]; then
				container_user_custom_home="$2"
				shift
				shift
			fi
			;;
		-p | --pull)
			container_always_pull=1
			shift
			;;
		--nvidia)
			shift
			nvidia=1
			;;
		-Y | --yes)
			non_interactive=1
			shift
			;;
		--volume)
			if [ -n "$2" ]; then
				container_manager_additional_flags="${container_manager_additional_flags} ${1} ${2}"
				shift
				shift
			fi
			;;
		-a | --additional-flags)
			if [ -n "$2" ]; then
				container_manager_additional_flags="${container_manager_additional_flags} ${2}"
				shift
				shift
			fi
			;;
		-ap | --additional-packages)
			if [ -n "$2" ]; then
				container_additional_packages="${container_additional_packages} ${2}"
				shift
				shift
			fi
			;;
		--init-hooks)
			if [ -n "$2" ]; then
				container_init_hook="$2"
				shift
				shift
			fi
			;;
		--pre-init-hooks)
			if [ -n "$2" ]; then
				container_pre_init_hook="${2}"
				shift
				shift
			fi
			;;
		--) # End of all options.
			shift
			break
			;;
		-*) # Invalid options.
			printf >&2 "ERROR: Invalid flag '%s'\n\n" "$1"
			show_help
			exit 1
			;;
		*) # Default case: If no more options then break out of the loop.
			# If we have a flagless option and container_name is not specified
			# then let's accept argument as container_name
			if [ -n "$1" ]; then
				container_name="$1"
				shift
			else
				break
			fi
			;;
	esac
done

set -o errexit
set -o nounset
# set verbosity
if [ "${verbose}" -ne 0 ]; then
	set -o xtrace
fi

# If no clone option and no container image, let's choose a default image to use.
# Fedora toolbox is a sensitive default
if [ -z "${container_clone}" ] && [ -z "${container_image}" ]; then
	container_image="${container_image_default}"
fi

# If no name is specified and we're using the default container_image, then let's
# set a default name for the container, that is distinguishable from the default
# toolbx one. This will avoid problems when using both toolbx and distrobox on
# the same system.
if [ -z "${container_name}" ] && [ "${container_image}" = "${container_image_default}" ]; then
	container_name="${container_name_default}"
fi

# If no container_name is declared, we build our container name starting from the
# container image specified.
#
# Examples:
#	alpine -> alpine
#	ubuntu:20.04 -> ubuntu-20.04
#	registry.fedoraproject.org/fedora-toolbox:38 -> fedora-toolbox-38
#	ghcr.io/void-linux/void-linux:latest-full-x86_64 -> void-linux-latest-full-x86_64
if [ -z "${container_name}" ]; then
	container_name="$(basename "${container_image}" | sed -E 's/[:.]/-/g')"
fi

# We depend on a container manager let's be sure we have it
# First we use podman, else docker
case "${container_manager}" in
	autodetect)
		if command -v podman > /dev/null; then
			container_manager="podman"
		elif command -v docker > /dev/null; then
			container_manager="docker"
		fi
		;;
	podman)
		container_manager="podman"
		;;
	docker)
		container_manager="docker"
		;;
	*)
		printf >&2 "Invalid input %s.\n" "${container_manager}"
		printf >&2 "The available choices are: 'autodetect', 'podman', 'docker'\n"
		;;
esac

# Be sure we have a container manager to work with.
if ! command -v "${container_manager}" > /dev/null && [ "${dryrun}" -eq 0 ]; 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"
	printf >&2 "You can follow the documentation on:\n"
	printf >&2 "\tman distrobox-compatibility\n"
	printf >&2 "or:\n"
	printf >&2 "\thttps://github.com/89luca89/distrobox/blob/main/docs/compatibility.md\n"
	exit 127
fi
# add  verbose if -v is specified
if [ "${verbose}" -ne 0 ]; then
	container_manager="${container_manager} --log-level debug"
fi

# prepend sudo (or the specified sudo program) if we want podman or docker to be rootful
if [ "${rootful}" -ne 0 ]; then
	container_manager="${distrobox_sudo_program-} ${container_manager}"
fi

# Clone a container as a snapshot.
# Arguments:
#   None
# Outputs:
#   prints the image name of the newly cloned container
clone_container() {
	# We need to clone a container.
	# to do this we will commit the container and create a new tag. Then use it
	# as image for the new container.
	#
	# to perform this we first ensure the source container exists and that the
	# source container is stopped, else the clone will not work,
	container_source_status="$(${container_manager} inspect --type container \
		"${container_clone}" --format '{{.State.Status}}')"
	# If the container is not already running, we need to start if first
	if [ "${container_source_status}" = "running" ]; then
		printf >&2 "Container %s is running.\nPlease stop it first.\n" "${container_clone}"
		printf >&2 "Cannot clone a running container.\n"
		return 1
	fi

	# Now we can extract the container ID and commit it to use as source image
	# for the new container.
	container_source_id="$(${container_manager} inspect --type container \
		"${container_clone}" --format '{{.Id}}')"
	container_commit_tag="$(echo "${container_clone}:$(date +%F)" | tr '[:upper:]' '[:lower:]')"

	# Commit current container state to a new image tag
	printf >&2 "Duplicating %s...\n" "${container_clone}"
	if ! ${container_manager} container commit \
		"${container_source_id}" "${container_commit_tag}" > /dev/null; then

		printf >&2 "Cannot clone container: %s\n" "${container_clone}"
		return 1
	fi

	# Return the image tag to use for the new container creation.
	printf "%s" "${container_commit_tag}"
	return 0
}

# 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}.$(uname -n)\"
		--name \"${container_name}\"
		--privileged
		--security-opt label=disable
		--user root:root"

	if [ "${unshare_ipc}" -eq 0 ]; then
		result_command="${result_command}
			--ipc host"
	fi

	if [ "${unshare_netns}" -eq 0 ]; then
		result_command="${result_command}
			--network host"
	fi

	if [ "${init}" -eq 0 ]; then
		result_command="${result_command}
			--pid host"
	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.
	# Also mount in the container the distrobox-export and distrobox-host-exec
	# utilities.
	result_command="${result_command}
		--label \"manager=distrobox\"
		--env \"SHELL=${SHELL:-"/bin/bash"}\"
		--env \"HOME=${container_user_home}\"
		--volume /:/run/host:rslave
		--volume /dev:/dev:rslave
		--volume /sys:/sys:rslave
		--volume /tmp:/tmp:rslave
		--volume \"${distrobox_entrypoint_path}\":/usr/bin/entrypoint:ro
		--volume \"${distrobox_export_path}\":/usr/bin/distrobox-export:ro
		--volume \"${distrobox_hostexec_path}\":/usr/bin/distrobox-host-exec:ro
		--volume \"${container_user_home}\":\"${container_user_home}\":rslave"

	# This fix is needed as on Selinux systems, the host's selinux sysfs directory
	# will be mounted inside the rootless container.
	#
	# This works around this and allows the rootless container to work when selinux
	# policies are installed inside it.
	#
	# Ref. Podman issue 4452:
	#    https://github.com/containers/podman/issues/4452
	if [ -e "/sys/fs/selinux" ]; then
		result_command="${result_command}
			--volume /sys/fs/selinux"
	fi

	# This fix is needed as systemd (or journald) will try to set ACLs on this
	# path. For now overlayfs and fuse.overlayfs are not compatible with ACLs
	#
	# This works around this using an unnamed volume so that this path will be
	# mounted with a normal non-overlay FS, allowing ACLs and preventing errors.
	#
	# This work around works in conjunction with distrobox-init's package manager
	# setups.
	# So that we can use pre/post hooks for package managers to present to the
	# systemd install script a blank path to work with, and mount the host's
	# journal path afterwards.
	result_command="${result_command}
			--volume /var/log/journal"

	# In some systems, for example using sysvinit, /dev/shm is a symlink
	# to /run/shm, instead of the other way around.
	# Resolve this detecting if /dev/shm is a symlink and mount original
	# source also in the container.
	if [ -L "/dev/shm" ]; then
		result_command="${result_command}
			--volume $(realpath /dev/shm):$(realpath /dev/shm)"
	fi

	# If you are using NixOS, or have Nix or Guix installed, /nix and /gnu are volumes containing
	# you binaries and many configs.
	# They be mounted if you want to execute those binaries from within
	# the container. Therefore we need to mount as a volume, but only if they exists.
	nix_dirs="
		/nix
		/gnu
		/run/current-system/sw
	"
	for nix_dir in ${nix_dirs}; do
		if [ -d "${nix_dir}" ]; then
			result_command="${result_command}
				--volume ${nix_dir}:${nix_dir}"
		fi
	done

	# If we have a home prefix to use,
	#	1- override the HOME env variable
	#	2- export the DISTROBOX_HOST_HOME env variable pointing to original HOME
	# 	3- mount the home inside the container.
	if [ -n "${container_home_prefix}" ] && [ -z "${container_user_custom_home}" ]; then
		if [ ! -d "${container_home_prefix}/${container_name}" ]; then
			if ! mkdir -p "${container_home_prefix}/${container_name}"; then
				printf >&2 "Do you have permission to write to %s?\n" "${container_home_prefix}"
				exit 1
			fi
		fi
		result_command="${result_command}
			--env \"HOME=${container_home_prefix}/${container_name}\"
			--env \"DISTROBOX_HOST_HOME=${container_user_home}\"
			--volume ${container_home_prefix}/${container_name}:${container_home_prefix}/${container_name}:rslave"
	fi

	# If we have a custom home to use,
	#	1- override the HOME env variable
	#	2- export the DISTROBOX_HOST_HOME env variable pointing to original HOME
	# 	3- mount the custom home inside the container.
	if [ -n "${container_user_custom_home}" ]; then
		if [ ! -d "${container_user_custom_home}" ]; then
			if ! mkdir -p "${container_user_custom_home}"; then
				printf >&2 "Do you have permission to write to %s?\n" "${container_user_custom_home}"
				exit 1
			fi
		fi
		result_command="${result_command}
			--env \"HOME=${container_user_custom_home}\"
			--env \"DISTROBOX_HOST_HOME=${container_user_home}\"
			--volume \"${container_user_custom_home}:${container_user_custom_home}:rslave\""
	fi

	# Mount also the /var/home dir on ostree based systems
	# do this only if $HOME was not already set to /var/home/username
	if [ "${container_user_home}" != "/var/home/${container_user_name}" ] &&
		[ -d "/var/home/${container_user_name}" ]; then

		result_command="${result_command}
			--volume \"/var/home/${container_user_name}\":\"/var/home/${container_user_name}\":rslave"
	fi

	# 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}:rslave"
	fi

	# 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.
	# This is the bare minimum to ensure connectivity inside the container.
	# These files, will then be kept updated by the main loop every 15 seconds.
	if [ "${unshare_netns}" -eq 0 ]; then
		NET_FILES="
			/etc/hosts
			/etc/resolv.conf
		"
		for net_file in ${NET_FILES}; do
			if [ -e "${net_file}" ]; then
				result_command="${result_command}
					--volume ${net_file}:${net_file}:ro"
			fi
		done
	fi

	# These flags are not supported by docker, so we use them only if our
	# container manager is podman.
	if echo "${container_manager}" | grep -q "podman"; then
		result_command="${result_command}
			--ulimit host
			--annotation run.oci.keep_original_groups=1
			--mount type=devpts,destination=/dev/pts"
		if [ "${init}" -eq 1 ]; then
			result_command="${result_command}
				--systemd=always"
		fi
		# Use keep-id only if going rootless.
		if [ "${rootful}" -eq 0 ]; then
			result_command="${result_command}
				--userns keep-id"
		fi
	fi

	# Add additional flags
	result_command="${result_command} ${container_manager_additional_flags}"

	# Now execute the entrypoint, refer to `distrobox-init -h` for instructions
	#
	# Be aware that entrypoint corresponds to distrobox-init, the copying of it
	# inside the container is moved to distrobox-enter, in the start phase.
	# This is done to make init, export and host-exec location independent from
	# the host, and easier to upgrade.
	#
	# We set the entrypoint _before_ running the container image so that
	# we can override any user provided entrypoint if need be
	result_command="${result_command}
	--entrypoint /usr/bin/entrypoint
	${container_image}
		--verbose
		--name \"${container_user_name}\"
		--user ${container_user_uid}
		--group ${container_user_gid}
		--home \"${container_user_custom_home:-"${container_user_home}"}\"
		--init \"${init}\"
		--nvidia \"${nvidia}\"
		--pre-init-hooks \"${container_pre_init_hook}\"
		--additional-packages \"${container_additional_packages}\"
		-- '${container_init_hook}'
	"
	# use container_user_custom_home if defined, else fallback to normal 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}" ] || [ -z "${distrobox_export_path}" ]; then
	printf >&2 "Error: no distrobox-init found in %s\n" "${PATH}"
	exit 127
fi

# dry run mode, just generate the command and print it. No creation.
if [ "${dryrun}" -ne 0 ]; then
	if [ -n "${container_clone}" ]; then
		container_image="${container_clone}"
	fi
	cmd="$(generate_command)"
	cmd="$(echo "${cmd}" | sed 's/\t//g')"
	printf "%s\n" "${cmd}"
	exit 0
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\n"
	# If it's a rootful container AND user is not logged as root.
	if [ "${rootful}" -eq 1 ] && [ "${container_user_uid}" -ne 0 ]; then
		printf "distrobox enter --root %s\n\n" "${container_name}"
	# If user is logged as root OR it's a rootless container.
	elif [ "${container_user_uid}" -eq 0 ] || [ "${rootful}" -eq 0 ]; then
		printf "distrobox enter %s\n\n" "${container_name}"
	fi
	exit 0
fi

# if we are using the clone flag, let's set the image variable
# to the output of container duplication
if [ -n "${container_clone}" ]; then
	container_image="$(clone_container)"
fi
# First, check if the image exists in the host or auto-pull is enabled
# If not prompt to download it.
if [ "${container_always_pull}" -eq 1 ] ||
	! ${container_manager} inspect --type image "${container_image}" > /dev/null 2>&1; then

	# If we do auto-pull, don't ask questions
	if [ "${non_interactive}" -eq 1 ] || [ "${container_always_pull}" -eq 1 ]; then
		response="yes"
	else
		# Prompt to download it.
		printf >&2 "Image %s not found.\n" "${container_image}"
		printf >&2 "Do you want to pull the image now? [Y/n]: "
		read -r response
		response="${response:-"Y"}"
	fi

	# Accept only y,Y,Yes,yes,n,N,No,no.
	case "${response}" in
		y | Y | Yes | yes | YES)
			# Pull the image
			${container_manager} pull "${container_image}"
			;;
		n | N | No | 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,YES or n,N,No,no,NO.\nExiting.\n"
			exit 1
			;;
	esac
fi

# Generate the create command and run it
printf >&2 "Creating '%s' using image %s\t" "${container_name}" "${container_image}"
cmd="$(generate_command)"
# Eval the generated command. If successful display an helpful message.
# shellcheck disable=SC2086
if eval ${cmd} > /dev/null; then
	printf >&2 "\033[32m [ OK ]\n\033[0mDistrobox '%s' successfully created.\n" "${container_name}"
	printf >&2 "To enter, run:\n\n"
	# If it's a rootful container AND user is not logged as root.
	if [ "${rootful}" -eq 1 ] && [ "${container_user_uid}" -ne 0 ]; then
		printf "distrobox enter --root %s\n\n" "${container_name}"
	# If user is logged as root OR it's a rootless container.
	elif [ "${container_user_uid}" -eq 0 ] || [ "${rootful}" -eq 0 ]; then
		printf "distrobox enter %s\n\n" "${container_name}"
	fi

	# We've created the box, let's also create the entry
	if [ "${rootful}" -eq 0 ]; then
		if [ "${container_generate_entry}" -ne 0 ]; then
			"${distrobox_genentry_path}" "${container_name}"
		fi
	fi
	exit $?
fi
printf >&2 "\033[31m [ ERR ]\n\033[0m"
