mirror of
https://github.com/89luca89/distrobox.git
synced 2026-08-19 01:14:49 -05:00
249 lines
6.5 KiB
Bash
Executable File
249 lines
6.5 KiB
Bash
Executable File
#!/bin/sh
|
|
# POSIX
|
|
# Expected env variables:
|
|
# HOME
|
|
# USER
|
|
|
|
trap '[ "$?" -ne 0 ] && echo An error occurred' EXIT
|
|
|
|
# Defaults
|
|
verbose=0
|
|
|
|
# Print usage to stdout.
|
|
# Arguments:
|
|
# None
|
|
# Outputs:
|
|
# print usage with examples.
|
|
show_help() {
|
|
echo "USAGE:
|
|
distrobox-init --name $USER --user $(id -ru) --group $(id -rg) --home $HOME
|
|
|
|
Arguments:
|
|
--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
|
|
-v: show more verbosity
|
|
"
|
|
}
|
|
|
|
# Parse arguments
|
|
while :; do
|
|
case $1 in
|
|
-h | --help)
|
|
# Call a "show_help" function to display a synopsis, then exit.
|
|
show_help
|
|
exit
|
|
;;
|
|
-v)
|
|
shift
|
|
verbose=1
|
|
;;
|
|
-n | --name)
|
|
if [ -n "$2" ]; then
|
|
container_user_name="$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
|
|
;;
|
|
*) # 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}" ] && echo "Invalid arguments, missing username" && exit 1
|
|
[ -z "${container_user_uid}" ] && echo "Invalid arguments, missing user uid" && exit 1
|
|
[ -z "${container_user_gid}" ] && echo "Invalid arguments, missing user gud" && exit 1
|
|
[ -z "${container_user_home}" ] && echo "Invalid argument, missing user home" && exit 1
|
|
|
|
# Also check we're running inside a container and not on the host
|
|
if [ ! -f /run/.containerenv ]; then
|
|
echo "You must run $(basename "$0") inside a container..."
|
|
exit 1
|
|
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
|
|
echo "Cannot create mount target directory: ${target_dir}"
|
|
return 1
|
|
fi
|
|
elif [ -f "${source_dir}" ]; then
|
|
# exit if not successful
|
|
if ! touch "${target_dir}"; then
|
|
echo "Cannot create mount target file: ${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
|
|
if ! mount --rbind ${mount_o} "${source_dir}" "${target_dir}"; then
|
|
echo "Failed to bind mount ${source_dir} to ${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
|
|
|
|
echo "${group}"
|
|
return 0
|
|
)
|
|
|
|
# We will use host's dynamic config for some functionalities, ensure they are
|
|
# symlinks to host's file inside the container.
|
|
#
|
|
# We do this before checking dependencies, as network access will be needed to
|
|
# install them if not found.
|
|
HOST_LINKS="/etc/host.conf /etc/hosts /etc/resolv.conf /etc/localtime /etc/timezone"
|
|
for link in $HOST_LINKS; do
|
|
rm -f "${link}"
|
|
ln -s /run/host"${link}" "${link}"
|
|
done
|
|
|
|
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
|
|
echo "FAILED TO INSTALL BASE DEPENDENCIES"
|
|
# Exit as command not found
|
|
exit 127
|
|
fi
|
|
|
|
fi
|
|
|
|
# We'll also bind mount in READ-ONLY useful folders from the host
|
|
HOST_MOUNTS_RO="/etc/machine-id /var/lib/flatpak /var/lib/systemd/coredump /var/log/journal"
|
|
for mount_ro in $HOST_MOUNTS_RO; do
|
|
mount_bind /run/host"${mount_ro}" "${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 mount in $HOST_MOUNTS; do
|
|
mount_bind /run/host"${mount}" "${mount}" rw
|
|
done
|
|
|
|
# If not present, mount host's theme folder in ~/.themes
|
|
if mkdir -p "${container_user_home}/.themes/"; then
|
|
if ! ln -s /run/host/usr/share/themes/* "${container_user_home}/.themes/"; then
|
|
echo "Error: cannot link container's themes to host home."
|
|
fi
|
|
fi
|
|
# If not present, mount host's icons folder in ~/.themes
|
|
if mkdir -p "${container_user_home}/.icons/"; then
|
|
if ! ln -s /run/host/usr/share/icons/* "${container_user_home}/.icons/"; then
|
|
echo "Error: cannot link container's icons to host home."
|
|
fi
|
|
fi
|
|
|
|
# Ensure passwordless sudo is set up for user
|
|
echo "Defaults !fqdn" >>/etc/sudoers
|
|
echo "${container_user_name} ALL = (root) NOPASSWD:ALL" >>/etc/sudoers
|
|
|
|
# Let's add our user to the container
|
|
# if the user already exists, just add it to the sudoers groups
|
|
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
|
|
|
|
echo "container_setup_done"
|
|
# Keepalive loop
|
|
sleep infinity
|