#!/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
#	SHELL

trap '[ "$?" -ne 0 ] && printf "Error: An error occurred\n"' EXIT

# Defaults
init=0
init_hook=""
upgrade=0
pre_init_hook=""
verbose=0
version="1.4.2.1"
# Print usage to stdout.
# Arguments:
#   None
# Outputs:
#   print usage with examples.
show_help() {
	cat << EOF
distrobox version: ${version}

Usage:

	distrobox-init --name ${USER} --user $(id -ru) --group $(id -rg) --home ${HOME}

Options:

	--name/-n:		user name
	--user/-u:		uid of the user
	--group/-g:		gid of the user
	--home/-d:		path/to/home of the user
	--help/-h:		show this message
	--init/-I:		whether to use or not init
	--pre-init-hooks:	commands to execute prior to init
	--upgrade/-U:		run init in upgrade mode
	--verbose/-v:		show more verbosity
	--version/-V:		show version
	--:			end arguments execute the rest as command to execute during init
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)
			shift
			verbose=1
			;;
		-V | --version)
			printf "distrobox: %s\n" "${version}"
			exit 0
			;;
		-U | --upgrade)
			shift
			upgrade=1
			;;
		-n | --name)
			if [ -n "$2" ]; then
				container_user_name="$2"
				shift
				shift
			fi
			;;
		-i | --init)
			if [ -n "$2" ]; then
				init="$2"
				shift
				shift
			fi
			;;
		-d | --home)
			if [ -n "$2" ]; then
				container_user_home="$2"
				shift
				shift
			fi
			;;
		-u | --user)
			if [ -n "$2" ]; then
				container_user_uid="$2"
				shift
				shift
			fi
			;;
		-g | --group)
			if [ -n "$2" ]; then
				container_user_gid="$2"
				shift
				shift
			fi
			;;
		--pre-init-hooks)
			if [ -n "$2" ]; then
				pre_init_hook="$2"
			fi
			shift
			shift
			;;
		--)
			shift
			init_hook=$*
			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.
			break ;;
	esac
done

# Check we're running inside a container and not on the host
if [ ! -f /run/.containerenv ] && [ ! -f /.dockerenv ]; then
	printf >&2 "You must run %s inside a container!\n" " $(basename "$0")"
	printf >&2 "distrobox-init should only be used as an entrypoint for a distrobox!\n\n"
	printf >&2 "This is not intended to be used manually, but instead used by distrobox-enter\n"
	printf >&2 "to set up the container's entrypoint.\n"
	exit 126
fi

# Ensure the foundamental variables are set and not empty, we will not proceed if
# they are not all set.
if [ "${upgrade}" -eq 0 ]; then
	[ -z "${container_user_gid}" ] && printf "Error: Invalid arguments, missing user gud\n" && exit 2
	[ -z "${container_user_home}" ] && printf "Error: Invalid argument, missing user home\n" && exit 2
	[ -z "${container_user_name}" ] && printf "Error: Invalid arguments, missing username\n" && exit 2
	[ -z "${container_user_uid}" ] && printf "Error: Invalid arguments, missing user uid\n" && exit 2
fi
set -o errexit
set -o nounset
# set verbosity
if [ "${verbose}" -ne 0 ]; then
	set -o xtrace
fi

# Bind mount or error.
# Arguments:
#   source_dir
#	target_dir
#	mount_flags -> optional
# Outputs:
#   No output if all ok
#	Error if not
mount_bind() (
	source_dir="$1"
	target_dir="$2"
	mount_flags="$3"

	# if source dir doesn't exist, just exit normally
	! [ -d "${source_dir}" ] && ! [ -f "${source_dir}" ] && return 0

	# if target_dir exists, check if it is a mountpoint and umount it.
	if [ -e "${target_dir}" ] && findmnt "${target_dir}" > /dev/null; then
		umount "${target_dir}"
	fi
	# if the source_dir exists, then create the target_dir
	if [ -d "${source_dir}" ]; then
		# exit if not successful
		if ! mkdir -p "${target_dir}"; then
			printf "Warning: cannot create mount target directory: %s\n" "${target_dir}"
			return 1
		fi
	# if instead it's a file, create it with touch
	elif [ -f "${source_dir}" ]; then
		# exit if not successful
		if ! touch "${target_dir}"; then
			printf "Warning: cannot create mount target file: %s\n" "${target_dir}"
			return 1
		fi
	fi

	# Add mountflags if needed, if no are specificed, use rslave as default.
	if [ "${mount_flags}" = "" ]; then
		mount_flags="rslave"
	fi
	# bind mount source_dir to target_dir, return error if not successful
	if ! mount --rbind -o "${mount_flags}" "${source_dir}" "${target_dir}"; then
		printf "Warning: failed to bind mount %s to %s\n" "${source_dir}" "${target_dir}"
		return 1
	fi

	return 0
)

if [ -n "${pre_init_hook}" ]; then
	printf "distrobox: Executing pre-init hooks...\n"
	# execute pre-init hooks if specified
	# shellcheck disable=SC2086
	eval ${pre_init_hook}
fi

###############################################################################
printf "distrobox: Installing basic packages...\n"
# Extract shell name from the $SHELL environment variable
# If not present as package in the container, we want to install it.
shell_pkg="$(basename "${SHELL:-"bash"}")"
# Ash shell is an exception, it is not a standalone package, but part of busybox.
# for this reason, use this quirk to adjust the package name.
if [ "${shell_pkg}" = "ash" ]; then
	shell_pkg="busybox"
fi

# Check if dependencies are met for the script to run.
if [ "${upgrade}" -ne 0 ] || ! command -v find || ! command -v mount || ! command -v passwd ||
	! command -v sudo || ! command -v useradd || ! command -v diff ||
	! command -v pinentry || ! command -v wget || ! command -v curl ||
	! command -v less || ! command -v bc || ! command -v time || ! command -v lsof ||
	! command -v "${shell_pkg}"; then

	# Detect the available package manager
	# install minimal dependencies needed to bootstrap the container:
	#	the same shell that's on the host
	#	sudo, mount, find
	#	passwd, groupadd and useradd
	if command -v apk; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			apk upgrade
			exit
		fi
		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		if ! apk add "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		apk add \
			"${shell_pkg}" \
			bc \
			curl \
			diffutils \
			findutils \
			gnupg \
			less \
			lsof \
			ncurses \
			pinentry \
			procps \
			shadow \
			sudo \
			util-linux \
			wget \
			vte3
		# These are graphics drivers for 3d applications
		# shellcheck disable=SC2046
		apk add \
			$(apk search -q mesa-dri) \
			$(apk search -q mesa-vulkan) \
			vulkan-loader

	elif command -v apt-get; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			apt update
			apt upgrade -y
			exit
		fi
		# In Ubuntu official images, dpkg is configured to ignore locale and docs
		# This however, results in a rather poor out-of-the-box experience
		# So, let's enable them.
		rm -f /etc/dpkg/dpkg.cfg.d/excludes

		export DEBIAN_FRONTEND=noninteractive
		apt-get update
		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		if ! apt-get install -y "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		apt-get install -y \
			"${shell_pkg}" \
			apt-utils \
			bc \
			curl \
			dialog \
			diffutils \
			findutils \
			gnupg2 \
			less \
			libnss-myhostname \
			libvte-2.9[0-9]-common \
			libvte-common \
			lsof \
			ncurses-base \
			passwd \
			pinentry-curses \
			procps \
			sudo \
			time \
			wget \
			util-linux

		# These are graphics drivers for 3d applications
		apt-get install -y \
			libegl1-mesa \
			libgl1-mesa-glx
		# Older versions of debian/ubuntu don't have vulkan, check for it.
		if apt-cache show libvulkan1 > /dev/null; then
			apt-get install -y \
				libvulkan1 \
				mesa-vulkan-drivers
		fi

	elif command -v dnf; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			dnf upgrade -y
			exit
		fi
		# In dnf family official images, dnf is configured to ignore locale and docs
		# This however, results in a rather poor out-of-the-box experience
		# So, let's enable them.
		sed -i '/tsflags=nodocs/d' /etc/dnf/dnf.conf

		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		if ! dnf install -y "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		dnf install -y --allowerasing \
			"${shell_pkg}" \
			bc \
			curl \
			diffutils \
			dnf-plugins-core \
			findutils \
			gnupg2 \
			less \
			lsof \
			ncurses \
			passwd \
			pinentry \
			procps-ng \
			shadow-utils \
			sudo \
			time \
			util-linux \
			wget \
			vte-profile

		# These are graphics drivers for 3d applications
		if dnf list mesa-dri-drivers > /dev/null; then
			dnf install -y \
				mesa-dri-drivers \
				mesa-vulkan-drivers \
				vulkan
		fi

	elif command -v emerge; then
		# Check if the container we are using has a ::gentoo repo defined,
		# if it is defined and it is empty, then synchroznize it.
		gentoo_repo="$(portageq get_repo_path / gentoo)"
		if [ -n "${gentoo_repo}" ] && [ ! -e "${gentoo_repo}" ]; then
			emerge-webrsync
		fi
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			emerge --sync
			exit
		fi
		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		if ! emerge --ask=n --autounmask-continue --noreplace --quiet-build "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		emerge --ask=n --autounmask-continue --noreplace --quiet-build \
			"${shell_pkg}" \
			app-crypt/gnupg \
			sys-devel/bc \
			diffutils \
			findutils \
			less \
			ncurses \
			net-misc/curl \
			pinentry \
			procps \
			shadow \
			sudo \
			sys-process/lsof \
			util-linux \
			wget

	elif command -v microdnf; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			microdnf upgrade -y
			exit
		fi
		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		if ! microdnf install -y "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		microdnf install -y \
			"${shell_pkg}" \
			bc \
			diffutils \
			findutils \
			gnupg \
			less \
			lsof \
			ncurses \
			passwd \
			pinentry \
			procps \
			shadow-utils \
			sudo \
			time \
			wget \
			vte-profile \
			util-linux

		# These are graphics drivers for 3d applications
		# On these very minimal images, graphics can be abstent
		# from the repos, so let's ignore the error in this case.
		microdnf install -y \
			mesa-dri-drivers \
			mesa-vulkan-drivers \
			vulkan || :

	elif command -v pacman; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			pacman -Syyu --noconfirm
			exit
		fi
		# In archlinux  official images, pacman is configured to ignore locale and docs
		# This however, results in a rather poor out-of-the-box experience
		# So, let's enable them.
		sed -i "s|NoExtract.*||g" /etc/pacman.conf
		sed -i "s|NoProgressBar.*||g" /etc/pacman.conf

		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		pacman --noconfirm -Syyu
		if ! pacman -Sy --noconfirm "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		pacman -Sy --noconfirm \
			"${shell_pkg}" \
			bc \
			curl \
			diffutils \
			findutils \
			gnupg \
			less \
			lsof \
			ncurses \
			pinentry \
			procps-ng \
			shadow \
			sudo \
			time \
			util-linux \
			wget \
			vte-common

		# These are graphics drivers for 3d applications
		pacman -Sy --noconfirm \
			mesa \
			opengl-driver \
			vulkan-intel \
			vulkan-radeon

	elif command -v slackpkg; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			yes | slackpkg upgrade-all -default_answer=yes -batch=yes
			exit
		fi
		slackpkg update
		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		if ! yes | slackpkg install -default_answer=yes -batch=yes "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		yes | slackpkg install -default_answer=yes -batch=yes \
			"${shell_pkg}" \
			bc \
			curl \
			diffutils \
			findutils \
			gnupg2 \
			less \
			libvte-2 \
			lsof \
			ncurses \
			pinentry \
			procps \
			shadow \
			sudo \
			time \
			wget \
			util-linux

		# These are graphics drivers for 3d applications
		yes | slackpkg install -default_answer=yes -batch=yes \
			mesa

	elif command -v swupd; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			swupd update
			exit
		fi
		# Here we do not handle shell_pkg as shells are already all bundled
		# together in "shells"
		swupd bundle-add \
			bc \
			cryptography \
			curl \
			procps-ng \
			shells \
			sysadmin-basic \
			wget

		# These are graphics drivers for 3d applications
		swupd bundle-add \
			lib-opengl \
			devpkg-Vulkan-Loader

	elif command -v xbps-install; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			xbps-install -Syu
			exit
		fi
		# Ensure we avoid errors by keeping xbps updated
		xbps-install -Syu xbps
		# We have to lock this package, as it's incompatible with distrobox's
		# mount process.
		xbps-pkgdb -m repolock base-files
		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		if ! xbps-install -Sy "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		xbps-install -Sy \
			"${shell_pkg}" \
			bc \
			curl \
			diffutils \
			findutils \
			gnupg2 \
			less \
			lsof \
			ncurses-base \
			procps-ng \
			shadow \
			sudo \
			time \
			wget \
			util-linux

		# These are graphics drivers for 3d applications
		xbps-install -Sy mesa-dri

	elif command -v yum; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			yum upgrade -y
			exit
		fi
		# In yum family official images, yum is configured to ignore locale and docs
		# This however, results in a rather poor out-of-the-box experience
		# So, let's enable them.
		sed -i '/tsflags=nodocs/d' /etc/yum.conf

		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
		if ! yum install -y "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		yum install -y \
			"${shell_pkg}" \
			bc \
			curl \
			diffutils \
			findutils \
			gnupg2 \
			less \
			lsof \
			ncurses \
			passwd \
			pinentry \
			procps \
			shadow-utils \
			sudo \
			time \
			util-linux \
			wget \
			vte-profile

		# These are graphics drivers for 3d applications
		# yum distros are too old to have vulkan anyway.
		if yum list mesa-dri-drivers > /dev/null; then
			yum install -y \
				mesa-dri-drivers
		fi

	elif command -v zypper; then
		# If we need to upgrade, do it and exit, no further action required.
		if [ "${upgrade}" -ne 0 ]; then
			zypper dup -y
			exit
		fi
		if ! zypper install -y "${shell_pkg}"; then
			shell_pkg="bash"
		fi
		zypper install -y \
			"${shell_pkg}" \
			bc \
			curl \
			diffutils \
			findutils \
			gnupg \
			less \
			libvte-2* \
			lsof \
			ncurses \
			pinentry \
			procps \
			shadow \
			sudo \
			systemd \
			time \
			wget \
			util-linux \
			util-linux-systemd

		# These are graphics drivers for 3d applications
		zypper install -y \
			Mesa-dri \
			libvulkan1 \
			libvulkan_intel \
			libvulkan_radeon

		# In openSUSE official images, zypper is configured to ignore recommended
		# packages (i.e., weak dependencies). This however, results in a rather
		# poor out-of-the-box experience (e.g., when trying to run GUI apps).
		# So, let's enable them. For the same reason, we make sure we install
		# docs.
		sed -i 's/.*solver.onlyRequires.*/solver.onlyRequires = false/g' /etc/zypp/zypp.conf
		sed -i 's/.*rpm.install.excludedocs.*/rpm.install.excludedocs = no/g' /etc/zypp/zypp.conf
		# With recommended packages, something might try to pull in
		# parallel-printer-support which can't be installed in rootless podman.
		# Since we very much likely never need it, just lock it
		zypper al parallel-printer-support
		# Check if shell_pkg is available in distro's repo. If not we
		# fall back to bash, and we set the SHELL variable to bash so
		# that it is set up correctly for the user.
	else
		printf "Error: could not find a supported package manager.\n"
		printf "Error: could not set up base dependencies.\n"
		# Exit as command not found
		exit 127
	fi
	# Ash shell is an exception, it is not a standalone package, but part of busybox.
	# for this reason, use this quirk to adjust the package name.
	if [ "${shell_pkg}" = "ash" ]; then
		SHELL="/bin/sh"
	else
		SHELL="$(command -v "${shell_pkg}")"
	fi
fi
###############################################################################

###############################################################################
printf "distrobox: Setting up read-only mounts...\n"
# We'll also bind mount in READ-ONLY useful directories from the host
HOST_MOUNTS_RO="
	/var/lib/flatpak
	/var/lib/systemd/coredump
	/var/log/journal"
for host_mount_ro in ${HOST_MOUNTS_RO}; do
	mount_bind /run/host"${host_mount_ro}" "${host_mount_ro}" ro
done
###############################################################################

###############################################################################
printf "distrobox: Setting up read-write mounts...\n"
# We'll also bind mount READ-WRITE useful mountpoints to pass external drives and libvirt from
# the host to the container
HOST_MOUNTS="
	/etc/host.conf
	/etc/machine-id
	/media
	/mnt
	/run/libvirt
	/run/media
	/run/systemd/journal
	/run/systemd/seats
	/run/systemd/sessions
	/run/systemd/users
	/run/systemd/resolve/
	/run/netconfig/
	/run/udev
	/srv
	/var/lib/libvirt
	/var/mnt"
for host_mount in ${HOST_MOUNTS}; do
	if ! mount_bind /run/host"${host_mount}" "${host_mount}" rw; then
		printf "Warning: Cannot bind mount %s to /run/host%s\n" "${host_mount}" "${host_mount}"
	fi
done

HOST_MOUNTS_RO_INIT="
		/etc/localtime
		/run/systemd/journal
		/run/systemd/resolve
		/run/systemd/seats
		/run/systemd/sessions
		/run/systemd/users
		/var/lib/systemd/coredump
		/var/log/journal"

# On some ostree systems, home is in /var/home, but most of the software expects
# it to be in /home. In the hosts systems this is fixed by using a symlink.
# Do something similar here with a bind mount.
if [ -e "/var/home/${container_user_name}" ] && [ ! -e "/home/${container_user_name}" ]; then
	if ! mount_bind "/run/host/var/home/${container_user_name}" "/home/${container_user_name}" rw; then
		printf "Warning: Cannot bind mount %s to /run/host%s\n" "/var/home" "/home"
	fi
fi
###############################################################################

###############################################################################
printf "distrobox: Setting up host's sockets integration...\n"
# 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/host/run -name 'user' \
	-prune -o -path /run/host/run/media \
	-prune -o -name 'nscd' \
	-prune -o -name 'bees' \
	-prune -o -name 'system_bus_socket' \
	-prune -o -type s -print \
	2> /dev/null || :)"

# we're excluding system dbus socket and nscd socket here. Including them will
# create many problems with package managers thinking they have access to
# system dbus or user auth cache misused.
for host_socket in ${host_sockets}; do
	container_socket="$(printf "%s" "${host_socket}" | sed 's|/run/host||g')"
	# Check if the socket already exists or the symlink already esists
	if [ ! -S "${container_socket}" ] && [ ! -L "${container_socket}" ]; then
		# link it.
		rm -f "${container_socket}"
		mkdir -p "$(dirname "${container_socket}")"
		if ! ln -s "${host_socket}" "${container_socket}"; then
			printf "Warning: Cannot link socket %s to %s\n" "${host_socket}" "${container_socket}"
		fi
	fi
done
###############################################################################

###############################################################################
printf "distrobox: Integrating host's themes, icons, fonts...\n"
# Themes and icons integration works using a bind mount inside the container
# of the host's themes and icons directory. This ensures that the host's home will
# not be littered with files and directories and broken symlinks.
if ! mount_bind "/run/host/usr/share/themes" "/usr/local/share/themes" rw; then
	printf "Warning: Cannot bind mount /run/host/usr/share/themes to /usr/local/share/themes\n"
	printf "Warning: Themes integration with the host is disabled.\n"
fi
if ! mount_bind "/run/host/usr/share/icons" "/usr/local/share/icons" rw; then
	printf "Warning: Cannot bind mount /run/host/usr/share/icons to /usr/local/share/icons\n"
	printf "Warning: Icons integration with the host is disabled.\n"
fi
if ! mount_bind "/run/host/usr/share/fonts" "/usr/local/share/fonts" rw; then
	printf "Warning: Cannot bind mount /run/host/usr/share/fonts to /usr/local/share/fonts\n"
	printf "Warning: Fonts integration with the host is disabled.\n"
fi
###############################################################################

printf "distrobox: Setting up package manager exceptions...\n"

###############################################################################
# In case of an RPM distro, we can specify that our bind_mount directories
# are in fact net shares. This prevents conflicts during package installations.
if [ -d "/usr/lib/rpm/macros.d/" ]; then
	printf "distrobox: Setting up rpm exceptions...\n"
	# Loop through all the environment vars
	# and export them to the container.
	net_mounts=""
	for net_mount in \
		${HOST_MOUNTS_RO} ${HOST_MOUNTS} \
		'/dev' '/proc' '/sys' '/tmp' \
		'/etc/host.conf' '/etc/hosts' '/etc/resolv.conf' '/etc/localtime' \
		'/usr/share/zoneinfo'; do

		net_mounts="${net_mount}:${net_mounts}"

	done
	net_mounts=${net_mounts%?}
	cat << EOF > /usr/lib/rpm/macros.d/macros.distrobox
%_netsharedpath ${net_mounts}
EOF

fi
###############################################################################

###############################################################################
# In case of an DEB distro, we can specify that our bind_mount directories
# have to be ignored. This prevents conflicts during package installations.
if [ -d "/etc/dpkg/dpkg.cfg.d/" ]; then
	# Loop through all the environment vars
	# and export them to the container.
	printf "distrobox: Setting up dpkg exceptions...\n"
	printf "" > /etc/dpkg/dpkg.cfg.d/00_distrobox
	for net_mount in ${HOST_MOUNTS_RO} ${HOST_MOUNTS}; do
		printf "path-exclude %s/*\n" "${net_mount}" >> /etc/dpkg/dpkg.cfg.d/00_distrobox
	done

	# Also we put a hook to clear some critical paths that do not play well
	# with read only filesystems, like systemd.
	if [ -d "/etc/apt/apt.conf.d/" ]; then
		printf "distrobox: Setting up apt hooks...\n"
		printf "" > /etc/apt/apt.conf.d/00_distrobox
		for init_mount in ${HOST_MOUNTS_RO_INIT}; do
			printf 'DPkg::Pre-Invoke {"if findmnt %s >/dev/null; then umount %s; fi";};\n' \
				"${init_mount}" "${init_mount}" >> /etc/apt/apt.conf.d/00_distrobox
			printf 'DPkg::Post-Invoke {"if [ -e /run/host/%s ]; then mount --rbind /run/host/%s %s; fi";};\n' \
				"${init_mount}" "${init_mount}" "${init_mount}" >> /etc/apt/apt.conf.d/00_distrobox
		done
	fi
fi
###############################################################################

###############################################################################
# Workarounds for pacman. We need to exclude the paths by using a pre-hook to umount them and a
# post-hook to remount them. Additionally we neutralize the systemd-post-hooks as they do not
# work on a rootless container system.
if [ -d "/usr/share/libalpm/scripts" ]; then
	printf "distrobox: Setting up pacman exceptions...\n"
	printf "#!/bin/sh\n" > /usr/share/libalpm/scripts/00_distrobox_pre_hook.sh
	printf "#!/bin/sh\n" > /usr/share/libalpm/scripts/01_distrobox_post_hook.sh
	printf "#!/bin/sh\n" > /usr/share/libalpm/scripts/02_distrobox_post_hook.sh
	chmod +x /usr/share/libalpm/scripts/*distrobox*.sh
	for net_mount in ${HOST_MOUNTS_RO}; do
		printf "if findmnt %s >/dev/null; then umount %s; fi\n" \
			"${net_mount}" "${net_mount}" >> /usr/share/libalpm/scripts/00_distrobox_pre_hook.sh
		printf "if [ -e /run/host/%s ]; then mount --rbind -o ro /run/host/%s %s; fi\n" \
			"${net_mount}" "${net_mount}" "${net_mount}" >> /usr/share/libalpm/scripts/02_distrobox_post_hook.sh
	done
	# in case we're not using an init image, neutralize systemd post installation hooks
	# so that we do not encounter problems along the way.
	# This will be removed if we're using --init.
	cat << EOF >> /usr/share/libalpm/scripts/01_distrobox_post_hook.sh
	echo -e '#!/bin/sh\nexit 0' > /usr/share/libalpm/scripts/systemd-hook;
EOF
	# create hooks files for them
	printf "distrobox: Setting up pacman hooks...\n"
	rm -f /usr/share/libalpm/hooks/*distrobox*
	for hook in 00_distrobox_pre_hook 01_distrobox_post_hook 02_distrobox_post_hook; do
		when="PostTransaction"
		[ -z "${hook##*pre*}" ] && when="PreTransaction"
		cat << EOF > "/usr/share/libalpm/hooks/${hook}.hook"
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = *
[Action]
Description = Distrobox hook ${hook}...
When = ${when}
Exec = /usr/share/libalpm/scripts/${hook}.sh
EOF
	done
fi
###############################################################################

printf "distrobox: Setting up sudo...\n"
mkdir -p /etc/sudoers.d
# Do not check fqdn when doing sudo, it will not work anyways
if ! grep -q 'Defaults !fqdn' /etc/sudoers.d/sudoers; then
	printf "Defaults !fqdn\n" >> /etc/sudoers.d/sudoers
fi
# Ensure passwordless sudo is set up for user
if ! grep -q "\"${container_user_name}\" ALL = (root) NOPASSWD:ALL" /etc/sudoers.d/sudoers; then
	printf "\"%s\" ALL = (root) NOPASSWD:ALL\n" "${container_user_name}" >> /etc/sudoers.d/sudoers
fi

printf "distrobox: Setting up groups...\n"
# If not existing, ensure we have a group for our user.
if ! grep -q "^${container_user_name}:" /etc/group; then
	if ! groupadd --force --gid "${container_user_gid}" "${container_user_name}"; then
		# It may occur that we have users with unsupported user name (eg. on LDAP or AD)
		# So let's try and force the group creation this way.
		printf "%s:x:%s:" "${container_user_name}" "${container_user_gid}" >> /etc/group
	fi
fi

printf "distrobox: Setting up users...\n"

# Setup kerberos integration with the host
if [ -d "/run/host/var/kerberos" ] &&
	[ -d "/etc/krb5.conf.d" ] &&
	[ ! -e "/etc/krb5.conf.d/kcm_default_ccache" ]; then

	cat << EOF > "/etc/krb5.conf.d/kcm_default_ccache"
# # To disable the KCM credential cache, comment out the following lines.
[libdefaults]
    default_ccache_name = KCM:
EOF
fi

# Let's add our user to the container. if the user already exists, enforce properties.
#
# In case of AD or LDAP usernames, it is possible we will have a backslach in the name.
# In that case grep would fail, so we replace the backslash with a point to make the regex work.
# shellcheck disable=SC1003
if ! grep -q "^$(printf '%s' "${container_user_name}" | tr '\\' '.'):" /etc/passwd; then
	if ! useradd \
		--home-dir "${container_user_home}" \
		--no-create-home \
		--shell "${SHELL:-"/bin/bash"}" \
		--uid "${container_user_uid}" \
		--gid "${container_user_gid}" \
		"${container_user_name}"; then

		printf "Warning: there was a problem setting up the user\n"
		printf "Warning: trying manual addition\n"
		printf "%s:x:%s:%s:%s:%s:%s" \
			"${container_user_name}" "${container_user_uid}" \
			"${container_user_gid}" "${container_user_name}" \
			"${container_user_home}" "${SHELL:-"/bin/bash"}" >> /etc/passwd
		printf "%s::1::::::" "${container_user_name}" >> /etc/shadow
	fi
# Ensure we're not using the specified SHELL. Run it only once, so that future
# user's preferences are not overwritten at each start.
elif [ ! -e /etc/passwd.done ]; then
	# This situation is presented when podman or docker already creates the user
	# for us inside container. We should modify the user's prepopulated shadowfile
	# entry though as per user's active preferences.
	if ! usermod \
		--home "${container_user_home}" \
		--shell "${SHELL:-"/bin/bash"}" \
		--uid "${container_user_uid}" \
		--gid "${container_user_gid}" \
		"${container_user_name}"; then

		printf "Warning: there was a problem setting up the user\n"
	fi
	touch /etc/passwd.done
fi

# We generate a random password to initialize the entry for the user and root.
temporary_password="$(cat /proc/sys/kernel/random/uuid)"
printf "%s\n%s\n" "${temporary_password}" "${temporary_password}" | passwd root
printf "%s:%s" "${container_user_name}" "${temporary_password}" | chpasswd -e
# Delete password for root and user
printf "%s:" "root" | chpasswd -e
printf "%s:" "${container_user_name}" | chpasswd -e

# If we do not have profile files in the home, we should copy the
# skeleton files, if present.
# Ensure we copy only if the dotfile is not already present.
mkdir -p /etc/profile.d
printf "test -z \"\$USER\" && USER=\"\$(id -un 2> /dev/null)\"\n" > /etc/profile.d/distrobox_profile.sh
printf "test -z \"\$UID\"  && readonly  UID=\"\$(id -ur 2> /dev/null)\"\n" >> /etc/profile.d/distrobox_profile.sh
printf "test -z \"\$EUID\" && readonly EUID=\"\$(id -u  2> /dev/null)\"\n" >> /etc/profile.d/distrobox_profile.sh
if [ -n "${DISTROBOX_HOST_HOME:-}" ] && [ -d "/etc/skel" ]; then
	skel_files="$(find /etc/skel/ -type f || :)"
	for skel_file in ${skel_files}; do
		if [ ! -f "${container_user_home}/$(basename "${skel_file}")" ] &&
			[ ! -L "${container_user_home}/$(basename "${skel_file}")" ]; then

			cp "${skel_file}" "${container_user_home}"
			chown "${container_user_uid}":"${container_user_gid}" \
				"${container_user_home}/$(basename "${skel_file}")"

		fi
	done
fi

printf "distrobox: Executing init hooks...\n"
# execute eventual init hooks if specified
# shellcheck disable=SC2086
eval ${init_hook}

# If init support is disabled, let's do our routine to keep the container
# up, running and in sync with host.
if [ "${init}" -eq 0 ]; then
	printf "container_setup_done\n"
	# Keepalive loop
	HOST_WATCH="
		/etc/hosts
		/etc/localtime
		/etc/resolv.conf
	"
	# disable verbose logging for this phase.
	set +x
	while true; do
		# Let's check for changes every 15 seconds.
		# This way we can dynamically keep hosts, dns and timezone setups
		# in sync with host, without having permissions problems:
		#	- symlink will fail with "Device or Resource busy"
		#	- bindmount will need a container restart on changes
		for file_watch in ${HOST_WATCH}; do
			# do stuff, only if we need to.
			file_watch_src="/run/host${file_watch}"
			# check if the target file is a symlink and take the source
			if [ -e "${file_watch_src}" ]; then
				if [ -L "${file_watch_src}" ]; then
					file_watch_src="$(readlink -f "/run/host${file_watch}")"
					# if it's an absolute link, we need to append /run/host ourselves.
					if ! echo "${file_watch_src}" | grep -q "/run/host"; then
						file_watch_src="/run/host${file_watch_src}"
					fi
				fi
				if ! diff "${file_watch}" "${file_watch_src}" > /dev/null; then
					# We only do this, if the file is a bind mount in the first place.
					# This could be useful for init-hooks that involve umounting those
					# files so that can be separated from the host.
					findmnt "${file_watch}" > /dev/null &&
						umount "${file_watch}" &&
						mount_bind "/run/host${file_watch}" "${file_watch}" rw
				fi
			fi
		done
		sleep 15
	done
fi

# If we're here, the init support has been enabled.
printf  "distrobox: Setting up init system...\n"
# some of this directories are needed by
# the init system. If they're mounts, there might
# be problems. Let's unmount them.
for host_mount in  ${HOST_MOUNTS_RO_INIT}; do
	if findmnt "${host_mount}" > /dev/null; then umount "${host_mount}"; fi
done
if command -v systemctl 2> /dev/null; then
	# Cleanup systemd to not interfere with the host
	# /etc/systemd/system/*.wants/
	UNIT_TARGETS="
		/usr/lib/systemd/system/basic.target.wants/*
		/usr/lib/systemd/system/local-fs.target.wants/*
		/usr/lib/systemd/system/multi-user.target.wants/*
		/usr/lib/systemd/system/plymouth*
		/usr/lib/systemd/system/sockets.target.wants/*initctl*
		/usr/lib/systemd/system/sockets.target.wants/*udev*
		/usr/lib/systemd/system/sysinit.target.wants/*
		/usr/lib/systemd/system/systemd-tmpfiles*
		/usr/lib/systemd/system/systemd-update-utmp*
		/usr/lib/systemd/system/NetworkManager-wait-online.service
		/usr/lib/systemd/system/auditd.service
		/usr/lib/systemd/system/libstoragemgmt.service
		/usr/lib/systemd/system/mcelog.service
	"
	# shellcheck disable=SC2086,SC2044
	for unit in $(find ${UNIT_TARGETS} 2> /dev/null); do
		systemctl mask "$(basename "${unit}")"
	done
fi
# We don't want these workarounds anymore
rm  -f /run/systemd/coredump
rm  -f /run/systemd/io.system.ManagedOOM
rm  -f /run/systemd/notify
rm  -f /run/systemd/private
rm  -f /etc/apt/apt.conf.d/00_distrobox
rm  -f /usr/share/libalpm/hooks/*distrobox*.hook
rm  -f /usr/share/libalpm/scripts/*distrobox*.sh
# Remove /dev/console when using init systems, this will confuse host system if
# we use rootful containers
touch /var/container-console
mount --rbind /var/container-console /dev/console
# Now we can launch init
printf  "distrobox: Firing up init system...\n"
printf "container_setup_done\n"
exec  /sbin/init
