Files
distrobox/distrobox-init
T

244 lines
6.3 KiB
Bash
Executable File

#!/bin/sh
# POSIX
# Expected env variables:
# HOME
# USER
# SHELL
trap '[ "$?" -ne 0 ] && printf "An error occurred\n"' EXIT
# Defaults
verbose=0
version="distrobox_version_placeholder"
# 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
--verbose/-v: show more verbosity
--version/-V: show version
"
EOF
}
# Parse arguments
while :; do
case $1 in
-h | --help)
# Call a "show_help" function to display a synopsis, then exit.
show_help
exit 0
;;
-v | --verbose)
shift
verbose=1
;;
-V | --version)
printf "distrobox: %s\n" "${version}"
exit 0
;;
-n | --name)
if [ -n "$2" ]; then
container_user_name="$2"
s${container_user_name}hift
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
;;
*) # Default case: If no more options then break out of the loop.
break ;;
esac
done
# Ensure the foundamental variables are set and not empty, we will not proceed if
# they are not all set.
[ -z "${container_user_name}" ] && printf >&2 "Invalid arguments, missing username\n" && exit 2
[ -z "${container_user_uid}" ] && printf >&2 "Invalid arguments, missing user uid\n" && exit 2
[ -z "${container_user_gid}" ] && printf >&2 "Invalid arguments, missing user gud\n" && exit 2
[ -z "${container_user_home}" ] && printf >&2 "Invalid argument, missing user home\n" && exit 2
# Also check we're running inside a container and not on the host
if [ ! -f /run/.containerenv ]; then
printf >&2 "You must run %s inside a container...\n" " $(basename "$0")"
exit 126
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"
mount_o=""
# if source dir doesn't exist, just exit normally
! [ -d "${source_dir}" ] && ! [ -f "${source_dir}" ] && return 0
# 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 >&2 "Cannot create mount target directory: %s\n" "${target_dir}"
return 1
fi
elif [ -f "${source_dir}" ]; then
# exit if not successful
if ! touch "${target_dir}"; then
printf >&2 "Cannot create mount target file: %s\n" "${target_dir}"
return 1
fi
fi
# Add mountflags if needed
[ "${mount_flags}" = "" ] || mount_o="-o ${mount_flags}"
# bind mount source_dir to target_dir, return error if not successful
# shellcheck disable=SC2086
if ! mount --rbind ${mount_o} "${source_dir}" "${target_dir}"; then
printf >&2 "Failed to bind mount %s to %s\n" "${source_dir}" "${target_dir}"
return 1
fi
return 0
)
# Return sudoers groups.
# Arguments:
# None
# Outputs:
# return all sudoers groups, error if not present.
list_sudo_groups() (
group=""
if grep -q "sudo:" /etc/group; then
group="sudo"
elif grep -q "wheel:" /etc/group; then
group="wheel"
else
# No group for root, create one
groupadd wheel
group="wheel"
fi
printf "%s" "${group}"
return 0
)
# 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}")"
# Check if dependencies are met for the script to run.
if ! command -v mount || ! command -v passwd ||
! command -v sudo || ! command -v useradd ||
! command -v usermod || ! command -v "${shell_pkg}"; then
# check which pkg manager we have at disposal
# and make sure we have all the dependencies we need to function.
if command -v apk; then
apk add --no-cache sudo shadow procps util-linux "${shell_pkg}"
elif command -v apt-get; then
apt-get update && apt-get install -y --no-install-recommends sudo apt-utils passwd procps util-linux "${shell_pkg}"
elif command -v dnf; then
dnf install -y --setopt=install_weak_deps=False sudo shadow-utils passwd procps-ng util-linux "${shell_pkg}"
elif command -v pacman; then
pacman -Sy --noconfirm sudo procps-ng shadow util-linux "${shell_pkg}"
elif command -v yum; then
yum install -y --setopt=install_weak_deps=False sudo shadow-utils passwd procps util-linux "${shell_pkg}"
elif command -v zypper; then
zypper install -y --no-recommends sudo shadow procps util-linux "${shell_pkg}"
else
printf >&2 "FAILED TO INSTALL BASE DEPENDENCIES\n"
# Exit as command not found
exit 127
fi
fi
# We'll also bind mount in READ-ONLY useful directories from the host
HOST_MOUNTS_RO="/etc/machine-id /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
# We'll also bind mount READ-WRITE useful mountpoints to pass external drives and libvirt from
# the host to the container
HOST_MOUNTS="/media /mnt /run/systemd/journal /run/libvirt /var/mnt /var/lib/libvirt"
for host_mount in ${HOST_MOUNTS}; do
mount_bind /run/host"${host_mount}" "${host_mount}" rw
done
# Do not check fqdn when doing sudo, it will not work anyways
printf "Defaults !fqdn\n" >>/etc/sudoers
# Ensure passwordless sudo is set up for user
printf "%s ALL = (root) NOPASSWD:ALL\n" "${container_user_name}" >>/etc/sudoers
# Let's add our user to the container
# if the user already exists, just add it to the sudoers groups
if ! grep -q "${container_user_name}" /etc/group; then
groupadd --force --gid "${container_user_uid}" "${container_user_name}"
fi
if ! useradd \
--home-dir "${container_user_home}" \
--no-create-home \
--shell /bin/bash \
--uid "${container_user_uid}" \
--gid "${container_user_gid}" \
--groups "$(list_sudo_groups)" \
"${container_user_name}"; then
usermod -aG "$(list_sudo_groups)" "${container_user_name}"
fi
# Delete password for root and user
passwd --delete "${container_user_name}"
passwd --delete root
printf "container_setup_done\n"
# Keepalive loop
sleep infinity