#!/usr/bin/bash# This file is part of Cockpit.## Copyright (C) 2018 Red Hat, Inc.## Cockpit is free software; you can redistribute it and/or modify it# under the terms of the GNU Lesser General Public License as published by# the Free Software Foundation; either version 2.1 of the License, or# (at your option) any later version.## Cockpit 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# Lesser General Public License for more details.## You should have received a copy of the GNU Lesser General Public License# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.# Run a local bridge, web server, and browser for a particular Cockpit page.# This is useful for integration into .desktop files, for systems which don't# have cockpit.socket enabled. The web server and browser run in an unshared# network namespace, and thus are totally isolated from everything else.## Usage:# cockpit-desktop /cockpit/@localhost/system/index.html -t default -r 0# For testing purposes one can also make cockpit-ws bind to all addresses# (this is insecure and should not be used in prodcuction):# cockpit-desktop /cockpit/@localhost/system/index.html -t default -r 1#set -euBROWSER_HOME=""THEME_ID="default"WEBUI_REMOTE=0WEBUI_NOAUTH=0while getopts t:r:n: optiondocase "${option}"int)THEME_ID=${OPTARG};;r)WEBUI_REMOTE=${OPTARG};;n)WEBUI_NOAUTH=${OPTARG};;*) echo "Usage: $0 [-t THEME_ID] [-r WEBUI_REMOTE] [-n WEBUI_NOAUTH]" >&2exit 1 ;;esacdoneURL_PATH="/cockpit/@localhost/anaconda-webui/index.html"REMOTE_AUTH_DROPIN="/etc/anaconda/cockpit/conf.d/50-remote-auth.conf"REMOTE_AUTH_SOURCE="/usr/share/anaconda/cockpit/conf.d/50-remote-auth.conf"WEBUI_AUTH=0if [[ "$WEBUI_REMOTE" == "1" && "$WEBUI_NOAUTH" == "0" ]]; thencp "$REMOTE_AUTH_SOURCE" "$REMOTE_AUTH_DROPIN"WEBUI_AUTH=1elserm -f "$REMOTE_AUTH_DROPIN"fiecho "WEBUI_AUTH=$WEBUI_AUTH" > /tmp/webui-cockpit-ws.envecho "WEBUI_REMOTE=$WEBUI_REMOTE" >> /tmp/webui-cockpit-ws.envsystemctl start webui-cockpit-wsif [[ "$WEBUI_REMOTE" == "1" ]]; thensystemctl start webui-tls-proxyfi# Function to setup user-related environmentsetup_user_environment() {# PKEXEC_UID is the uid for "gnome-initial-setup" or "liveuser"# depending on how the installer gets started.## It's unset on non-live-images, so we just use the current user then (root)if [ -n "${PKEXEC_UID:-}" ]; then# If PKEXEC_UID is set, use the user associated with itINSTALLER_USER=$(id -n -u "${PKEXEC_UID}")else# Try to find another user who is not root and has /usr/bin/bash as their shellINSTALLER_USER=$(grep '/usr/bin/bash' /etc/passwd | grep -v '^root' | cut -d: -f1 | head -n 1)# If no such user is found, use the current user running the scriptif [ -z "${INSTALLER_USER:-}" ]; thenINSTALLER_USER=$(id -un)fifi# Get the home directory of the selected userHOME_DIR=$(grep "^$INSTALLER_USER:" /etc/passwd | cut -d: -f6)# Check if the home directory exists, otherwise create itif [ ! -d "$HOME_DIR" ]; thenecho "The home directory $HOME_DIR does not exist. Creating it." >&2mkdir -p "$HOME_DIR"chown "$INSTALLER_USER" "$HOME_DIR"elseecho "Home directory $HOME_DIR already exists." >&2fi# Return the user for further useecho "$INSTALLER_USER"}get_default_browser() {# Read the default browser from the anaconda runtime configuration file but wait till it's available# FIXME: Related to: https://issues.redhat.com/browse/INSTALLER-4223timeout=60elapsed=0while [ ! -f /run/anaconda/anaconda.conf ]; doif [ "$elapsed" -ge "$timeout" ]; thenecho "Timed out waiting for anaconda.conf to be available." >&2exit 1fisleep 1elapsed=$((elapsed + 1))done# FIXME: Should be provided as an argument to this script instead of grepping the config fileDEFAULT_BROWSER=$(grep '^webui_web_engine =' /run/anaconda/anaconda.conf | cut -d'=' -f2)if [ -n "$DEFAULT_BROWSER" ]; thenecho "Default browser set in anaconda.conf: $DEFAULT_BROWSER" >&2DEFAULT_BROWSER=$(echo "$DEFAULT_BROWSER" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')echo "$DEFAULT_BROWSER"elseecho "No default browser set in anaconda.conf, using firefox." >&2echo "firefox"fi}prepare_firefox_cmd() {INSTALLER_USER=$1INSTALLER_XDG_RUNTIME_DIR=$2# prepare empty firefox profile dir with theme based on the passed profile id# INSTALLER_XDG_RUNTIME_DIR is created by the caller before this runs.FIREFOX_THEME_DIR="/usr/share/anaconda/firefox-theme"FIREFOX_PROFILE_PATH="${INSTALLER_XDG_RUNTIME_DIR}/anaconda/firefox-profile"# make sure the profile directory exists and is emptyif [ -d ${FIREFOX_PROFILE_PATH} ]thenecho "Cleaning up existing Anaconda Firefox profile directory." >&2rm -rf ${FIREFOX_PROFILE_PATH}fipkexec --user "${INSTALLER_USER}" mkdir -p ${FIREFOX_PROFILE_PATH}# populate the profile directory with our custom Firefox theme# - theme id is passed as the second argument of this scriptTHEME_PATH="${FIREFOX_THEME_DIR}/${THEME_ID}"pkexec --user "${INSTALLER_USER}" cp -a "${THEME_PATH}/." ${FIREFOX_PROFILE_PATH}# set environment for FirefoxBROWSER_ENV=(MOZ_APP_TITLE="" MOZ_APP_REMOTINGNAME="liveinst" MOZ_GTK_TITLEBAR_DECORATION=client)BROWSER=(/usr/bin/env "${BROWSER_ENV[@]}" /usr/bin/firefox --new-instance --profile "${FIREFOX_PROFILE_PATH}")declare -p BROWSER}if [[ "$WEBUI_REMOTE" == "1" ]]then/bin/sleep infinity &BLOCK_ON_PID=$!elseINSTALLER_USER=$(setup_user_environment)INSTALLER_UID=$(id -u "${INSTALLER_USER}")INSTALLER_XDG_RUNTIME_DIR="/run/user/${INSTALLER_UID}"if [ ! -d "${INSTALLER_XDG_RUNTIME_DIR}" ]; thenmkdir -p "${INSTALLER_XDG_RUNTIME_DIR}"chown "${INSTALLER_USER}" "${INSTALLER_XDG_RUNTIME_DIR}"chmod 0700 "${INSTALLER_XDG_RUNTIME_DIR}"fi# if we have netcat, use it for waiting until ws is upif type nc >/dev/null 2>&1; thenfor _ in `seq 10`; donc -z 127.0.0.1 80 && breaksleep 0.5;doneelse# otherwise, just wait a bitsleep 3fiif user_env_output=$(pkexec --user "${INSTALLER_USER}" env XDG_RUNTIME_DIR="${INSTALLER_XDG_RUNTIME_DIR}" systemctl --user show-environment 2>/dev/null); thenreadarray -t user_environment <<< "$user_env_output"USER_SESSION_AVAILABLE=1elseecho "No systemd user session available, continuing without user session environment." >&2user_environment=()USER_SESSION_AVAILABLE=0fiUSER_GRAPHICAL_SESSION_AVAILABLE=0if [ "$USER_SESSION_AVAILABLE" = "1" ] && [ -n "${PKEXEC_UID:-}" ]; then# Live ISO only: reuse the desktop user's session when it has a display.if printf '%s\n' "${user_environment[@]}" | grep -qE '^(WAYLAND_DISPLAY|DISPLAY)='; thenUSER_GRAPHICAL_SESSION_AVAILABLE=1fifiif [ "$USER_GRAPHICAL_SESSION_AVAILABLE" = "1" ]; thenfor var in DISPLAY WAYLAND_DISPLAY XDG_RUNTIME_DIR DBUS_SESSION_BUS_ADDRESS XAUTHORITY; doif [ -n "${!var:-}" ] && ! printf '%s\n' "${user_environment[@]}" | grep -q "^${var}="; thenuser_environment+=("${var}=${!var}")fidonefiDEFAULT_BROWSER_CMD=$(get_default_browser)echo "Using default browser: ${DEFAULT_BROWSER_CMD}" >&2if [[ "${DEFAULT_BROWSER_CMD}" == "firefox" ]]; theneval "$(prepare_firefox_cmd "$INSTALLER_USER" "$INSTALLER_XDG_RUNTIME_DIR")"echo "Using Firefox browser command: ${BROWSER[@]}" >&2elseBROWSER=(${DEFAULT_BROWSER_CMD})echo "Using default browser command: ${BROWSER[@]}" >&2fi# start browser in a temporary home dir, so that it does not interfere with your real oneBROWSER_HOME=$(mktemp --directory --tmpdir cockpit.desktop.XXXXXX)chown "${INSTALLER_USER}" "${BROWSER_HOME}"WEBUI_URL=http://127.0.0.1"$URL_PATH"if [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; thenif [ "$USER_GRAPHICAL_SESSION_AVAILABLE" = "1" ]; thenHOME="$BROWSER_HOME" \pkexec --user "$INSTALLER_USER" env XDG_CURRENT_DESKTOP=GNOME "${user_environment[@]}" "${BROWSER[@]}" "$WEBUI_URL" &BLOCK_ON_PID=$!elsetype dbus-run-session >/dev/null 2>&1 || { echo "Error: dbus-run-session is not ready, cannot continue." >&2; exit 1; }ROOT_XDG_RUNTIME_DIR="/run/user/0"ROOT_WAYLAND_SOCKET="$(find "${ROOT_XDG_RUNTIME_DIR}" -maxdepth 1 -type s -name 'wl-sysinstall-*' | head -n 1)"if [ -z "${ROOT_WAYLAND_SOCKET}" ]; thenecho "Error: root Wayland socket not found under ${ROOT_XDG_RUNTIME_DIR}" >&2exit 1fichmod 711 "${ROOT_XDG_RUNTIME_DIR}"chmod a+rw "${ROOT_WAYLAND_SOCKET}"HOME="$BROWSER_HOME" \pkexec --user "$INSTALLER_USER" env \XDG_CURRENT_DESKTOP=GNOME \XDG_SESSION_TYPE=wayland \XDG_RUNTIME_DIR="${INSTALLER_XDG_RUNTIME_DIR}" \WAYLAND_DISPLAY="${ROOT_WAYLAND_SOCKET}" \dbus-run-session -- "${BROWSER[@]}" "$WEBUI_URL" \>/tmp/webui-browser.log 2>&1 &BLOCK_ON_PID=$!fielseecho "Error: no graphical session detected (DISPLAY and WAYLAND_DISPLAY are unset)." >&2exit 1fifi# Cleanup functioncleanup() {set +ekill $BLOCK_ON_PID[[ -n $BROWSER_HOME ]] && rm -rf $BROWSER_HOME}trap 'cleanup' EXIT INT QUIT PIPEwait $BLOCK_ON_PID