mirror of
https://github.com/89luca89/distrobox.git
synced 2026-08-19 01:14:49 -05:00
* Fix gentoo support this patch includes multiple fixes: - the local repository no longer gets automatically updated. Other package managers dont do this either and the output can include something about an error, that isnt actually a problem, which causes distrobox-init to fail. - there are multiple curl packages for gentoo. net-misc/curl is the web request tool. - vte-profile doesnt exist for gentoo. The closest package is probably vte. - some packages compilation will report errors, which get ignored by the build tools, so dont matter, but the mention will cause distrobox-init to abort. To prevent this --quiet-build is used to supress to messages from compilation
881 lines
26 KiB
Bash
Executable File
881 lines
26 KiB
Bash
Executable File
#!/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=""
|
|
pre_init_hook=""
|
|
verbose=0
|
|
version="1.2.16"
|
|
# 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
|
|
--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
|
|
;;
|
|
-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"
|
|
shift
|
|
shift
|
|
fi
|
|
;;
|
|
--)
|
|
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
|
|
|
|
set -o errexit
|
|
set -o nounset
|
|
# set verbosity
|
|
if [ "${verbose}" -ne 0 ]; then
|
|
set -o xtrace
|
|
fi
|
|
|
|
# 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.
|
|
[ -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
|
|
|
|
# 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}" ] && mountpoint "${target_dir}"; 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 ! 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 "${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
|
|
# 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="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
apk add \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
ncurses \
|
|
pinentry \
|
|
procps \
|
|
shadow \
|
|
sudo \
|
|
util-linux \
|
|
wget \
|
|
vte3
|
|
|
|
elif command -v apt-get; then
|
|
# 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="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
apt-get install -y \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
libnss-myhostname \
|
|
libvte-common \
|
|
ncurses-base \
|
|
passwd \
|
|
pinentry-curses \
|
|
procps \
|
|
sudo \
|
|
wget \
|
|
util-linux
|
|
|
|
elif command -v dnf; then
|
|
# 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="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
dnf install -y --allowerasing \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
ncurses \
|
|
passwd \
|
|
pinentry \
|
|
procps-ng \
|
|
shadow-utils \
|
|
sudo \
|
|
util-linux \
|
|
wget \
|
|
vte-profile
|
|
|
|
elif command -v emerge; then
|
|
# 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 "${shell_pkg}"; then
|
|
SHELL="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
emerge --ask=n --autounmask-continue --quiet-build \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
net-misc/curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
ncurses \
|
|
pinentry \
|
|
procps \
|
|
shadow \
|
|
sudo \
|
|
util-linux \
|
|
wget \
|
|
vte
|
|
|
|
elif command -v microdnf; then
|
|
# 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="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
microdnf install -y \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
ncurses \
|
|
passwd \
|
|
pinentry \
|
|
procps \
|
|
shadow-utils \
|
|
sudo \
|
|
wget \
|
|
util-linux
|
|
# vte-profile
|
|
|
|
elif command -v pacman; then
|
|
# 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
|
|
|
|
# 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="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
pacman -Sy --noconfirm \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
ncurses \
|
|
pinentry \
|
|
procps-ng \
|
|
shadow \
|
|
sudo \
|
|
util-linux \
|
|
wget \
|
|
vte-common
|
|
|
|
elif command -v slackpkg; then
|
|
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="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
yes | slackpkg install -default_answer=yes -batch=yes \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
libvte-2 \
|
|
ncurses \
|
|
pinentry \
|
|
procps \
|
|
shadow \
|
|
sudo \
|
|
wget \
|
|
util-linux
|
|
|
|
elif command -v swupd; then
|
|
# 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
|
|
|
|
elif command -v xbps-install; then
|
|
# 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="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
xbps-install -Sy \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
ncurses-base \
|
|
procps-ng \
|
|
shadow \
|
|
sudo \
|
|
wget \
|
|
util-linux
|
|
|
|
elif command -v yum; then
|
|
# 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="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
yum install -y \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
ncurses \
|
|
passwd \
|
|
pinentry \
|
|
procps \
|
|
shadow-utils \
|
|
sudo \
|
|
util-linux \
|
|
wget \
|
|
vte-profile
|
|
|
|
elif command -v zypper; then
|
|
# 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.
|
|
if ! zypper install -y "${shell_pkg}"; then
|
|
SHELL="/bin/bash"
|
|
shell_pkg="bash"
|
|
fi
|
|
zypper install -y \
|
|
"${shell_pkg}" \
|
|
bc \
|
|
curl \
|
|
diffutils \
|
|
findutils \
|
|
less \
|
|
libvte-2* \
|
|
ncurses \
|
|
pinentry \
|
|
procps \
|
|
shadow \
|
|
sudo \
|
|
systemd \
|
|
wget \
|
|
util-linux
|
|
|
|
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
|
|
|
|
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
|
|
|
|
# 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 /home 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 -name 'nscd' \
|
|
-prune -o -name 'system_bus_socket' \
|
|
-prune -o -type s -print \
|
|
2> /dev/null || :)"
|
|
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
|
|
# Loop through all the environment vars
|
|
# and export them to the container.
|
|
set +o xtrace
|
|
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
|
|
# re-enable logging if it was enabled previously.
|
|
if [ "${verbose}" -ne 0 ]; then
|
|
set -o xtrace
|
|
fi
|
|
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.
|
|
set +o xtrace
|
|
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
|
|
# re-enable logging if it was enabled previously.
|
|
if [ "${verbose}" -ne 0 ]; then
|
|
set -o xtrace
|
|
fi
|
|
|
|
# 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
|
|
cat << EOF > /etc/apt/apt.conf.d/00_distrobox
|
|
DPkg::Pre-Invoke {"if mountpoint /var/log/journal; then umount /var/log/journal; fi";};
|
|
DPkg::Post-Invoke {"if [ -e /run/host/var/log/journal ]; then mount --rbind -o ro /run/host/var/log/journal /var/log/journal; fi";};
|
|
EOF
|
|
fi
|
|
fi
|
|
|
|
if [ -d "/usr/share/libalpm/scripts" ]; then
|
|
set +o xtrace
|
|
printf "#!/bin/sh\n" > /usr/share/libalpm/scripts/00_distrobox_pre_hook.sh
|
|
printf "#!/bin/sh\n" > /usr/share/libalpm/scripts/02_distrobox_post_hook.sh
|
|
for net_mount in ${HOST_MOUNTS_RO}; do
|
|
printf "if mountpoint %s; 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
|
|
# re-enable logging if it was enabled previously.
|
|
if [ "${verbose}" -ne 0 ]; then
|
|
set -o xtrace
|
|
fi
|
|
# in case we're not using an init image, neutralize systemd post installation hooks
|
|
# so that we do not encounter problems along the way.
|
|
cat << EOF > /usr/share/libalpm/scripts/01_distrobox_post_hook.sh
|
|
#!/bin/sh
|
|
if [ ! -e /run/systemd/system ]; then
|
|
echo -e '#!/bin/sh\nexit 0' > /usr/share/libalpm/scripts/systemd-hook;
|
|
fi
|
|
EOF
|
|
chmod +x /usr/share/libalpm/scripts/00_distrobox_pre_hook.sh
|
|
chmod +x /usr/share/libalpm/scripts/01_distrobox_post_hook.sh
|
|
chmod +x /usr/share/libalpm/scripts/02_distrobox_post_hook.sh
|
|
|
|
cat << EOF > /usr/share/libalpm/hooks/00-distrobox-pre.hook
|
|
[Trigger]
|
|
Operation = Install
|
|
Operation = Upgrade
|
|
Type = Package
|
|
Target = *
|
|
|
|
[Action]
|
|
Description = Distrobox pre-hooks...
|
|
When = PreTransaction
|
|
Exec = /usr/share/libalpm/scripts/00_distrobox_pre_hook.sh
|
|
EOF
|
|
|
|
cat << EOF > /usr/share/libalpm/hooks/00-distrobox-post.hook
|
|
[Trigger]
|
|
Operation = Install
|
|
Operation = Upgrade
|
|
Type = Package
|
|
Target = *
|
|
|
|
[Action]
|
|
Description = Distrobox post-hooks 1...
|
|
When = PostTransaction
|
|
Exec = /usr/share/libalpm/scripts/01_distrobox_post_hook.sh
|
|
EOF
|
|
|
|
cat << EOF > /usr/share/libalpm/hooks/99-distrobox-post.hook
|
|
[Trigger]
|
|
Operation = Install
|
|
Operation = Upgrade
|
|
Type = Package
|
|
Target = *
|
|
|
|
[Action]
|
|
Description = Distrobox post-hooks 2...
|
|
When = PostTransaction
|
|
Exec = /usr/share/libalpm/scripts/02_distrobox_post_hook.sh
|
|
EOF
|
|
|
|
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"
|
|
# Let's add our user to the container. if the user already exists, enforce properties.
|
|
if ! grep -q "^${container_user_name}:" /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
|
|
else
|
|
# 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
|
|
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 [ -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}
|
|
|
|
printf "container_setup_done\n"
|
|
|
|
if [ "${init}" -eq 0 ]; then
|
|
# 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.
|
|
if ! diff "${file_watch}" "/run/host${file_watch}" > /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.
|
|
mountpoint "${file_watch}" &&
|
|
umount "${file_watch}" &&
|
|
mount_bind "/run/host${file_watch}" "${file_watch}" rw
|
|
fi
|
|
done
|
|
sleep 15
|
|
done
|
|
else
|
|
# some of this directories are needed by
|
|
# the init system. If they're mounts, there might
|
|
# be problems. Let's unmount them.
|
|
printf "distrobox: Setting up init system...\n"
|
|
HOST_MOUNTS_RO_INIT="
|
|
/run/systemd/journal
|
|
/run/systemd/seats
|
|
/run/systemd/sessions
|
|
/run/systemd/system
|
|
/run/systemd/users
|
|
/var/lib/systemd/coredump
|
|
/var/log/journal"
|
|
for host_mount in ${HOST_MOUNTS_RO_INIT}; do
|
|
if mountpoint "${host_mount}"; then umount "${host_mount}"; fi
|
|
done
|
|
# Now we can launch init
|
|
printf "distrobox: Firing up init system...\n"
|
|
exec /sbin/init
|
|
fi
|