#!/usr/bin/env bash script_path="$(dirname "$(realpath "$0")")" . "$script_path/functions.sh" assert_prerequisites [ -z "$1" ] && die "Expecting 1 argument" image_name="$(relative_path_to_image_name "apps/$1")" # image_name is of the form dobu/xxxx, while for containers we want dobu-xxx container_name="${image_name/\//-}" home_path="$HOMEDIR_STORAGE/$1" log "Image name: $image_name" log "Container name: $container_name" log "Home directory path: $home_path" assert_image_exists "$image_name" remove_stale_container "$container_name" if container_exists "$container_name"; then log "$container_name is already running; re-executing entrypoint command" log "If this is not desired, please stop the application or run \`podman stop $container_name\` manually" podman exec -d "$container_name" $(container_entrypoint "$container_name") exit 0 fi if [ "$(get_image_label "$image_name" net.typeblog.dobu.unsafe_i_know_what_i_am_doing_allow_namespaces)" == "true" ]; then log "Enabling sub-namespaces support inside this container" log "This is considered UNSAFE; DO NOT USE if the app inside container does not do its own sandboxing" log "DO NOT USE if you don't trust sandboxing done by the app inside" update_podman_security_args "seccomp_unsafe.json" fi if [[ -n $PULSE_SERVER ]]; then # remove prefix host_pulse=${PULSE_SERVER#unix:} else # default guess host_pulse=$XDG_RUNTIME_DIR/pulse/native fi if is_in_array "$1" "${DISPLAY_SERVER_APP_ALLOWLIST[@]}"; then log "Allowing app $1 full access to Wayland / X11 sockets" WAYLAND_SRC="${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" if [ -z "$DISPLAY" ]; then # TODO: Maybe we can just ignore Xorg in these cases die "$$DISPLAY must be set for apps granted full Wayland / Xorg access" fi XORG_SRC="/tmp/.X11-unix/X${DISPLAY/:/}" if [ ! -S "${XORG_SRC}" ] || [ ! -S "${WAYLAND_SRC}" ]; then die "Wayland / Xorg sockets do not exist" fi else # Make sure we have Sommelier running first ensure_sommelier WAYLAND_SRC="$DOBU_TMP/xdg_runtime/wayland-1" XORG_SRC="$DOBU_TMP/X11-unix/X1" fi # Prepare $HOME for the container if [ ! -d "$home_path" ]; then if [ "$HOMEDIR_IS_BTRFS" == "true" ]; then log "Creating $home_path as a btrfs subvolume" btrfs subvol create "$home_path" else log "Creating $home_path" mkdir -p "$home_path" fi fi # Some containers want these default XDG directories to exist; make sure they do mkdir -p "$home_path/.config" mkdir -p "$home_path/.cache" mkdir -p "$home_path/.local/share" extra_args="" # Expose Pipewire socket if it exists if [ -S "$XDG_RUNTIME_DIR/pipewire-0" ]; then extra_args="$extra_args -v $XDG_RUNTIME_DIR/pipewire-0:/xdg_runtime/pipewire-0" fi # Check if we should allow /dev/input access if is_in_array "$1" "${DEV_INPUT_APP_ALLOWLIST[@]}"; then if [ -z "${DEV_INPUT_DEVICE_ALLOWLIST+x}" ]; then log "Granting full /dev/input access" log "Set DEV_INPUT_DEVICE_ALLOWLIST for more fine-grained control" extra_args="$extra_args -v /dev/input:/dev/input" else for device in "${DEV_INPUT_DEVICE_ALLOWLIST[@]}"; do device=$(realpath /dev/input/"$device") [[ ! "$device" =~ ^/dev/input/ ]] && continue log "Granting access to input device $device" extra_args="$extra_args -v $device:$device" done fi fi # The fun part: start the container! # Don't detach like we did with Sommelier, though podman run --rm "${podman_security_args[@]}" --name "$container_name" \ `# Create tmpfs mountpoints for runtime directories` \ --mount type=tmpfs,destination=/xdg_runtime,chown,tmpfs-mode=0700 \ --mount type=tmpfs,destination=/tmp/.X11-unix,chown,tmpfs-mode=0700 \ `# Pass through Sommelier Wayland socket` \ `# Note that XDG_RUNTIME_DIR is already set in the image` \ -v "${WAYLAND_SRC}":/xdg_runtime/wayland-0 \ -e WAYLAND_DISPLAY=wayland-0 \ `# Pass through Sommelier X11 socket` \ -v "${XORG_SRC}":/tmp/.X11-unix/X0 \ -e DISPLAY=:0 \ `# DRM render nodes` \ -v /dev/dri:/dev/dri \ `# Pass through PulseAudio` \ -v "$host_pulse":/xdg_runtime/pulse/native \ -e PULSE_SERVER=unix:/xdg_runtime/pulse/native \ `# $HOME` \ -v "$home_path":/home/user \ `# Miscellaneous` \ -e XDG_SESSION_TYPE=wayland \ -e TZ="$(date +%Z)" \ `# SHM is needed by some browser engines (such as CEF used by Steam)`\ --shm-size=1G \ `# Scaling parameters` \ -e GDK_SCALE="$GDK_SCALE" \ -e QT_SCALE_FACTOR="$QT_SCALE_FACTOR" \ -e QT_SCREEN_SCALE_FACTORS="$QT_SCREEN_SCALE_FACTORS" \ -e QT_AUTO_SCREEN_SCALE_FACTOR="$QT_AUTO_SCREEN_SCALE_FACTOR" \ `# Use podman's init stub inside the container for better control` \ --init \ $extra_args "$image_name"