#!/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 . # POSIX # Optional env variables: # DBX_CONTAINER_MANAGER # DBX_VERBOSE # Despite of running this script via SUDO/DOAS being not supported (the # script itself will call the appropriate tool when necessary), we still want # to allow people to run it as root, logged in in a shell, and create rootful # containers. # # SUDO_USER is a variable set by SUDO and can be used to check whether the script was called by it. Same thing for DOAS_USER, set by DOAS. if { [ -n "${SUDO_USER}" ] || [ -n "${DOAS_USER}" ] } && [ "$(id -ru)" -eq 0 ]; then printf >&2 "Running %s via SUDO/DOAS is not supported. Instead, please try running:\n" "$(basename "${0}")" printf >&2 " %s --root %s\n" "$(basename "${0}")" "$*" exit 1 fi # Ensure we have our env variables correctly set [ -z "${USER}" ] && USER="$(id -run)" [ -z "${HOME}" ] && HOME="$(getent passwd "${USER}" | cut -d':' -f6)" [ -z "${SHELL}" ] && SHELL="$(getent passwd "${USER}" | cut -d':' -f7)" # If the user runs this script as root in a login shell, set rootful=1. # There's no need for them to pass the --root flag option in such cases. [ "$(id -ru)" -eq 0 ] && rootful=1 || rootful=0 extra_flags="" all=0 container_manager="autodetect" container_name_default="my-distrobox" delete=0 icon="auto" icon_default="${XDG_DATA_HOME:-${HOME}/.local/share}/icons/terminal-distrobox-icon.svg" verbose=0 online=0 version="1.8.2.2" # Source configuration files, this is done in an hierarchy so local files have # priority over system defaults # leave priority to environment variables. # # On NixOS, for the distrobox derivation to pick up a static config file shipped # by the package maintainer the path must be relative to the script itself. self_dir="$(dirname "$(realpath "$0")")" nix_config_file="${self_dir}/../share/distrobox/distrobox.conf" config_files=" ${nix_config_file} /usr/share/distrobox/distrobox.conf /usr/share/defaults/distrobox/distrobox.conf /usr/etc/distrobox/distrobox.conf /usr/local/share/distrobox/distrobox.conf /etc/distrobox/distrobox.conf ${XDG_CONFIG_HOME:-"${HOME}/.config"}/distrobox/distrobox.conf ${HOME}/.distroboxrc " for config_file in ${config_files}; do # Shellcheck will give error for sourcing a variable file as it cannot follow # it. We don't care so let's disable this linting for now. # shellcheck disable=SC1090 [ -e "${config_file}" ] && . "$(realpath "${config_file}")" done [ -n "${DBX_CONTAINER_MANAGER}" ] && container_manager="${DBX_CONTAINER_MANAGER}" [ -n "${DBX_VERBOSE}" ] && verbose="${DBX_VERBOSE}" # Fixup variable=[true|false], in case we find it in the config file(s) [ "${verbose}" = "true" ] && verbose=1 [ "${verbose}" = "false" ] && verbose=0 # show_help will print usage to stdout. # Arguments: # None # Expected global variables: # version: distrobox version # Expected env variables: # None # Outputs: # print usage with examples. show_help() { cat << EOF distrobox version: ${version} Usage: distrobox-generate-entry container-name [--delete] [--icon [auto,/path/to/icon]] Options: --help/-h: show this message --all/-a: perform for all distroboxes --delete/-d: delete the entry --icon/-i: specify a custom icon [/path/to/icon] (default auto) --root/-r: perform on rootful distroboxes --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) verbose=1 shift ;; -V | --version) printf "distrobox: %s\n" "${version}" exit 0 ;; -d | --delete) delete=1 shift ;; -a | --all) all=1 shift ;; -i | --icon) if [ -n "$2" ]; then icon="$2" shift shift fi ;; -r | --root) shift rootful=1 ;; --) # End of all options. shift 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. # If we have a flagless option and container_name is not specified # then let's accept argument as container_name if [ -n "$1" ]; then container_name="$1" shift else break fi ;; esac done set -o errexit set -o nounset # set verbosity if [ "${verbose}" -ne 0 ]; then set -o xtrace fi if [ -z "${container_name:-}" ]; then container_name="${container_name_default}" fi if [ "${all}" -ne 0 ]; then container_names="$(distrobox list --no-color | tail -n +2 | cut -d'|' -f2 | tr -d ' ')" for container_name in ${container_names}; do if [ "${delete}" -ne 0 ]; then "${0}" "${container_name}" --delete continue fi "${0}" "${container_name}" done exit fi # If we delete, just ask confirmation and exit. if [ "${delete}" -ne 0 ]; then rm -f "${XDG_DATA_HOME:-${HOME}/.local/share}/applications/${container_name}.desktop" exit fi if ! command -v curl > /dev/null && ! command -v wget > /dev/null; then printf >&2 "Icon generation depends on either curl or wget\n" printf >&2 "Fallbacking to default icon.\n" download="null" fi if command -v curl > /dev/null 2>&1; then download="curl --connect-timeout 3 --retry 1 -sLo" elif command -v wget > /dev/null 2>&1; then download="wget --timeout=3 --tries=1 -qO" fi # We depend on a container manager let's be sure we have it # First we use podman, else docker, else lilipod case "${container_manager}" in autodetect) if command -v podman > /dev/null; then container_manager="podman" container_manager_cp_command="podman cp" elif command -v podman-launcher > /dev/null; then container_manager="podman-launcher" container_manager_cp_command="podman-launcher cp" elif command -v docker > /dev/null; then container_manager="docker" container_manager_cp_command="docker cp -L" elif command -v lilipod > /dev/null; then container_manager="lilipod" container_manager_cp_command="lilipod cp" fi ;; podman) container_manager="podman" container_manager_cp_command="podman cp" ;; podman-launcher) container_manager="podman-launcher" container_manager_cp_command="podman-launcher cp" ;; lilipod) container_manager="lilipod" container_manager_cp_command="lilipod cp" ;; docker) container_manager="docker" container_manager_cp_command="docker cp -L" ;; *) printf >&2 "Invalid input %s.\n" "${container_manager}" printf >&2 "The available choices are: 'autodetect', 'podman', 'docker', 'lilipod'\n" ;; esac # Be sure we have a container manager to work with. if ! command -v "${container_manager}" > /dev/null; then # Error: we need at least one between docker, podman or lilipod. printf >&2 "Missing dependency: we need a container manager.\n" printf >&2 "Please install one of podman, docker or lilipod.\n" printf >&2 "You can follow the documentation on:\n" printf >&2 "\tman distrobox-compatibility\n" printf >&2 "or:\n" printf >&2 "\thttps://github.com/89luca89/distrobox/blob/main/docs/compatibility.md\n" exit 127 fi # add verbose if -v is specified if [ "${verbose}" -ne 0 ]; then container_manager="${container_manager} --log-level debug" container_manager_cp_command="${container_manager_cp_command} --log-level debug" fi # prepend sudo (or the specified sudo program) if we want our container manager to be rootful if [ "${rootful}" -ne 0 ]; then extra_flags="${extra_flags} --root" container_manager="${distrobox_sudo_program:-"sudo"} ${container_manager}" container_manager_cp_command="${distrobox_sudo_program:-"sudo"} ${container_manager_cp_command}" fi if ! ${container_manager} inspect --type container "${container_name}" > /dev/null; then printf >&2 "Cannot find container %s. Please create it first.\n" "${container_name}" exit 1 fi # Ensure the destination dir exists. mkdir -p "${XDG_DATA_HOME:-${HOME}/.local/share}/applications" mkdir -p "${XDG_DATA_HOME:-${HOME}/.local/share}/icons/distrobox" distrobox_path="$(dirname "$(realpath "${0}")")" entry_name="$(echo "${container_name}" | cut -c1 | tr "[:lower:]" "[:upper:]")$(echo "${container_name}" | cut -c2-)" if [ "${icon}" = "auto" ]; then # Set icon to the generic terminal as a fallback. icon="${icon_default}" # This is a NON comprehensive list of logos of the most popular distributions. If you find logos for # other supported distros, add it here. DISTRO_ICON_MAP=" alma:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/alma-distrobox.png alpine:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/alpine-distrobox.png alt:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/alt-distrobox.png arch:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/arch-distrobox.png centos:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/centos-distrobox.png clear--os:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/clear-distrobox.png debian:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/debian-distrobox.png deepin:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/deepin-distrobox.png fedora:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/fedora-distrobox.png gentoo:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/gentoo-distrobox.png kali:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/kali-distrobox.png kdeneon:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/kdeneon-distrobox.png opensuse-leap:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/opensuse-distrobox.png opensuse-tumbleweed:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/opensuse-distrobox.png rhel:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/redhat-distrobox.png rocky:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/rocky-distrobox.png ubuntu:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/ubuntu-distrobox.png vanilla:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/vanilla-distrobox.png void:https://raw.githubusercontent.com/89luca89/distrobox/main/docs/assets/png/distros/void-distrobox.png " # Try to detect container's distribution by using /etc/os-release ${container_manager_cp_command} "${container_name}":/etc/os-release /tmp/"${container_name}".os-release container_distro="$(grep "^ID=" /tmp/"${container_name}".os-release | cut -d'=' -f2- | sed "s|linux||g" | tr -d ' ' | tr -d '"')" if [ "${rootful}" -ne 0 ]; then ${distrobox_sudo_program:-"sudo"} rm -f /tmp/"${container_name}".os-release else rm -f /tmp/"${container_name}".os-release fi icon_url="$(echo "${DISTRO_ICON_MAP}" | grep "${container_distro}:" | cut -d':' -f2-)" # Distro not found in our map, fallback to generic icon if [ -z "${icon_url}" ]; then icon_url="https://raw.githubusercontent.com/89luca89/distrobox/main/icons/terminal-distrobox-icon.svg" container_distro="terminal-distrobox-icon" fi if [ -n "${icon_url}" ] && [ "${download}" != "null" ]; then icon_extension="${icon_url##*.}" if [ "${online}" -lt 1 ] && ${download} - "${icon_url}" > "${XDG_DATA_HOME:-${HOME}/.local/share}/icons/distrobox/${container_distro}.${icon_extension}"; then icon="${XDG_DATA_HOME:-${HOME}/.local/share}/icons/distrobox/${container_distro}.${icon_extension}" else # Wget failed for some reasons. Default to generic terminal icon as declared at the beginning. printf >&2 "Warning: Failed to download icon. Defaulting to generic one.\n" fi else # Distribution not found in the list. Default to generic terminal icon as declared at the beginning. printf >&2 "Warning: Distribution not found in default icon set. Defaulting to generic one.\n" fi fi cat << EOF > "${XDG_DATA_HOME:-${HOME}/.local/share}/applications/${container_name}.desktop" [Desktop Entry] Name=${entry_name} GenericName=Terminal entering ${entry_name} Comment=Terminal entering ${entry_name} Categories=Distrobox;System;Utility Exec=${distrobox_path}/distrobox enter ${extra_flags} ${container_name} Icon=${icon} Keywords=distrobox; NoDisplay=false Terminal=true TryExec=${distrobox_path}/distrobox Type=Application Actions=Remove; [Desktop Action Remove] Name=Remove ${entry_name} from system Exec=${distrobox_path}/distrobox rm ${extra_flags} ${container_name} EOF