#!/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 . # 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 -eu BROWSER_HOME="" THEME_ID="default" WEBUI_REMOTE=0 WEBUI_NOAUTH=0 while getopts t:r:n: option do case "${option}" in t)THEME_ID=${OPTARG};; r)WEBUI_REMOTE=${OPTARG};; n)WEBUI_NOAUTH=${OPTARG};; *) echo "Usage: $0 [-t THEME_ID] [-r WEBUI_REMOTE] [-n WEBUI_NOAUTH]" >&2 exit 1 ;; esac done URL_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=0 if [[ "$WEBUI_REMOTE" == "1" && "$WEBUI_NOAUTH" == "0" ]]; then cp "$REMOTE_AUTH_SOURCE" "$REMOTE_AUTH_DROPIN" WEBUI_AUTH=1 else rm -f "$REMOTE_AUTH_DROPIN" fi echo "WEBUI_AUTH=$WEBUI_AUTH" > /tmp/webui-cockpit-ws.env echo "WEBUI_REMOTE=$WEBUI_REMOTE" >> /tmp/webui-cockpit-ws.env systemctl start webui-cockpit-ws if [[ "$WEBUI_REMOTE" == "1" ]]; then systemctl start webui-tls-proxy fi # Function to setup user-related environment setup_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 it INSTALLER_USER=$(id -n -u "${PKEXEC_UID}") else # Try to find another user who is not root and has /usr/bin/bash as their shell INSTALLER_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 script if [ -z "${INSTALLER_USER:-}" ]; then INSTALLER_USER=$(id -un) fi fi # Get the home directory of the selected user HOME_DIR=$(grep "^$INSTALLER_USER:" /etc/passwd | cut -d: -f6) # Check if the home directory exists, otherwise create it if [ ! -d "$HOME_DIR" ]; then echo "The home directory $HOME_DIR does not exist. Creating it." >&2 mkdir -p "$HOME_DIR" chown "$INSTALLER_USER" "$HOME_DIR" else echo "Home directory $HOME_DIR already exists." >&2 fi # Return the user for further use echo "$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-4223 timeout=60 elapsed=0 while [ ! -f /run/anaconda/anaconda.conf ]; do if [ "$elapsed" -ge "$timeout" ]; then echo "Timed out waiting for anaconda.conf to be available." >&2 exit 1 fi sleep 1 elapsed=$((elapsed + 1)) done # FIXME: Should be provided as an argument to this script instead of grepping the config file DEFAULT_BROWSER=$(grep '^webui_web_engine =' /run/anaconda/anaconda.conf | cut -d'=' -f2) if [ -n "$DEFAULT_BROWSER" ]; then echo "Default browser set in anaconda.conf: $DEFAULT_BROWSER" >&2 DEFAULT_BROWSER=$(echo "$DEFAULT_BROWSER" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') echo "$DEFAULT_BROWSER" else echo "No default browser set in anaconda.conf, using firefox." >&2 echo "firefox" fi } prepare_firefox_cmd() { INSTALLER_USER=$1 INSTALLER_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 empty if [ -d ${FIREFOX_PROFILE_PATH} ] then echo "Cleaning up existing Anaconda Firefox profile directory." >&2 rm -rf ${FIREFOX_PROFILE_PATH} fi pkexec --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 script THEME_PATH="${FIREFOX_THEME_DIR}/${THEME_ID}" pkexec --user "${INSTALLER_USER}" cp -a "${THEME_PATH}/." ${FIREFOX_PROFILE_PATH} # set environment for Firefox BROWSER_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=$! else INSTALLER_USER=$(setup_user_environment) INSTALLER_UID=$(id -u "${INSTALLER_USER}") INSTALLER_XDG_RUNTIME_DIR="/run/user/${INSTALLER_UID}" if [ ! -d "${INSTALLER_XDG_RUNTIME_DIR}" ]; then mkdir -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 up if type nc >/dev/null 2>&1; then for _ in `seq 10`; do nc -z 127.0.0.1 80 && break sleep 0.5; done else # otherwise, just wait a bit sleep 3 fi if user_env_output=$(pkexec --user "${INSTALLER_USER}" env XDG_RUNTIME_DIR="${INSTALLER_XDG_RUNTIME_DIR}" systemctl --user show-environment 2>/dev/null); then readarray -t user_environment <<< "$user_env_output" USER_SESSION_AVAILABLE=1 else echo "No systemd user session available, continuing without user session environment." >&2 user_environment=() USER_SESSION_AVAILABLE=0 fi USER_GRAPHICAL_SESSION_AVAILABLE=0 if [ "$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)='; then USER_GRAPHICAL_SESSION_AVAILABLE=1 fi fi if [ "$USER_GRAPHICAL_SESSION_AVAILABLE" = "1" ]; then for var in DISPLAY WAYLAND_DISPLAY XDG_RUNTIME_DIR DBUS_SESSION_BUS_ADDRESS XAUTHORITY; do if [ -n "${!var:-}" ] && ! printf '%s\n' "${user_environment[@]}" | grep -q "^${var}="; then user_environment+=("${var}=${!var}") fi done fi DEFAULT_BROWSER_CMD=$(get_default_browser) echo "Using default browser: ${DEFAULT_BROWSER_CMD}" >&2 if [[ "${DEFAULT_BROWSER_CMD}" == "firefox" ]]; then eval "$(prepare_firefox_cmd "$INSTALLER_USER" "$INSTALLER_XDG_RUNTIME_DIR")" echo "Using Firefox browser command: ${BROWSER[@]}" >&2 else BROWSER=(${DEFAULT_BROWSER_CMD}) echo "Using default browser command: ${BROWSER[@]}" >&2 fi # start browser in a temporary home dir, so that it does not interfere with your real one BROWSER_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:-}" ]; then if [ "$USER_GRAPHICAL_SESSION_AVAILABLE" = "1" ]; then HOME="$BROWSER_HOME" \ pkexec --user "$INSTALLER_USER" env XDG_CURRENT_DESKTOP=GNOME "${user_environment[@]}" "${BROWSER[@]}" "$WEBUI_URL" & BLOCK_ON_PID=$! else type 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}" ]; then echo "Error: root Wayland socket not found under ${ROOT_XDG_RUNTIME_DIR}" >&2 exit 1 fi chmod 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=$! fi else echo "Error: no graphical session detected (DISPLAY and WAYLAND_DISPLAY are unset)." >&2 exit 1 fi fi # Cleanup function cleanup() { set +e kill $BLOCK_ON_PID [[ -n $BROWSER_HOME ]] && rm -rf $BROWSER_HOME } trap 'cleanup' EXIT INT QUIT PIPE wait $BLOCK_ON_PID