1
0
mirror of https://github.com/pumpitupdev/pumptools.git synced 2024-11-24 07:00:09 +01:00
pumptools/dist/x11docker
icex2 d7b03a935a wip
some tests based of references: docker image with simply docker run
command works with nx2, though no keyboard input or sound

using x11docker, it might be easier to get the missing features though
i still need to figure out how to get glxgears to work at least
2021-02-27 00:34:52 +01:00

9002 lines
383 KiB
Bash
Executable File

#! /usr/bin/env bash
# x11docker
# Run GUI applications and desktop environments in Docker containers.
#
# - Runs additional X servers to circumvent common X security leaks.
# - Restricts container capabilities to enhance container security.
# - Container user is same as host user to avoid root in container.
# - Features e.g. sound, hardware acceleration and data storage.
#
# Run 'x11docker --help' or scroll down to read usage information.
# More documentation at: https://github.com/mviereck/x11docker
Version="6.7.0-beta"
# --enforce-i: Enforce running in interactive mode to allow commands tty and weston-launch in special setups.
grep -q -- "--enforce-i" <<< "$*" && case $- in
*i*) set +H ;;
*) exec bash --noprofile --norc --noediting -i -- "$0" "$@" ;;
esac
usage() { # --help: show usage information
echo "
x11docker: Run GUI applications and desktop environments in Docker containers.
Usage:
To run a Docker container on a new X server:
x11docker IMAGE
x11docker [OPTIONS] IMAGE [COMMAND]
x11docker [OPTIONS] -- IMAGE [COMMAND [ARG1 ARG2 ...]]
x11docker [OPTIONS] -- DOCKER_RUN_OPTIONS -- IMAGE [COMMAND [ARG1 ARG2 ...]]
To run a host application on a new X server:
x11docker [OPTIONS] --exe COMMAND
x11docker [OPTIONS] --exe -- COMMAND [ARG1 ARG2 ...]
To run only an empty new X server:
x11docker [OPTIONS] --xonly
x11docker always runs a fresh container from image and discards it afterwards.
Runs on Linux and (with some restrictions) on MS Windows. Not adapted for macOS.
Optional features:
* GPU hardware acceleration
* Sound with pulseaudio or ALSA
* Clipboard sharing
* Printer access
* Webcam access
* Persistent home folder
* Wayland support
* Language locale creation
* Several init systems and DBus in container
* Support of several container runtimes
Focus on security:
* Avoids X security leaks using additional X servers.
* Container user is same as host user to avoid root in container.
* Restricts container capabilities to bare minimum.
x11docker sets up an unprivileged container user with password 'x11docker'
and restricts container capabilities. Some applications might behave different
than with a regular 'docker run' command due to these security restrictions.
Achieve a less restricted setup with --cap-default, --sudouser or --user=root.
Dependencies on host:
For core functionality x11docker only needs bash, docker and an X server.
Depending on chosen options x11docker might need some additional tools.
It checks for them on startup and shows messages if some are missing.
Core list of recommended tools:
* Recommended to allow security and convenience:
X servers: xpra Xephyr nxagent
X tools: xauth xclip xrandr xhost xinit
* Advanced GPU support: weston Xwayland xpra xdotool
See also: https://github.com/mviereck/x11docker/wiki/Dependencies
Dependencies in image:
No dependencies in image except for a few feature options. Most important:
--gpu: OpenGL/MESA packages, collected often in 'mesa-utils' package.
--pulseaudio: Needs pulseaudio on host and pulseaudio client libs in image.
--printer: Needs cups on host and cups client libs in image.
See also: https://github.com/mviereck/x11docker/wiki/Dependencies
Options: (short options do not accept arguments)
--help Display this message and exit.
-e, --exe Execute host application instead of docker container.
--xonly Only create empty X server.
Basic settings:
-d, --desktop Indicate a desktop environment in image.
In that case important for automatical X server choice.
-i, --interactive Run with an interactive tty to allow shell commands.
Useful with commands like bash.
Host integration:
--alsa [=ALSA_CARD] Sound with ALSA. You can define a desired sound card
with ALSA_CARD. List of available sound cards: aplay -l
-c, --clipboard Share clipboard. Graphical clips with --xpra only.
-g, --gpu GPU access for hardware accelerated OpenGL rendering.
Works best with open source drivers on host and in image.
For closed source nvidia drivers regard terminal output.
-I, --network [=NET] Allow internet access. (Currently enabled by default,
will change in future.)
For optional argument NET see Docker documentation
of docker run option --network.
--network=none disables internet access. (future default)
-l, --lang [=LOCALE] Set language variable LANG=LOCALE in container.
Without arg LOCALE host variable --lang=\$LANG is used.
If LOCALE is missing in image, x11docker generates it
with 'localedef' in container (needs 'locales' package).
Examples for LOCALE: ru, en, de, zh_CN, cz, fr, fr_BE.
-P, --printer [=MODE] Share host printers through CUPS server.
Optional MODE can be 'socket' or 'tcp'. Default: socket
-p, --pulseaudio [=MODE] Sound with pulseaudio. Needs 'pulseaudio' on host
and in image. Optional arg MODE can be 'socket' or 'tcp'.
--webcam Share host webcam device files.
Shared host folders or Docker volumes:
-m, --home [=ARG] Create a persistant HOME folder for data storage.
Default: Uses ~/.local/share/x11docker/IMAGENAME.
ARG can be another host folder or a Docker volume.
(~/.local/share/x11docker has a softlink to ~/x11docker.)
(Use --homebasedir to change this base storage folder.)
--share ARG Share host file or folder ARG. Read-only with ARG:ro
Device files in /dev can be shared, too.
ARG can also be a Docker volume instead of a host folder.
X server options:
--auto Automatically choose X server (default). Influenced
noteable by options --desktop, --gpu, --wayland, --wm.
-h, --hostdisplay Share host display :0. Quite bad container isolation!
Least overhead of all X server options.
Some apps may fail due to restricted untrusted cookies.
Remove restrictions with option --clipboard.
-n, --nxagent Nested X server supporting seamless and --desktop mode.
Faster than --xpra, but can have compositing issues.
-Y, --weston-xwayland Desktop mode like --xephyr, but supports option --gpu.
Runs from console, within X and within Wayland.
-y, --xephyr Nested X server for --desktop mode. Without --desktop
a host window manager will be provided (option --wm).
-x, --xorg Core Xorg server. Runs ootb from console.
Switch tty with <CTRL><ALT><F1>....<F12>. Always switch
to a black tty before switching to X to avoid crashes.
-a, --xpra Nested X server supporting seamless and --desktop mode.
-A, --xpra-xwayland Like --xpra, but supports option --gpu.
Special X server options:
--kwin-xwayland Like --weston-xwayland, but using kwin_wayland
-t, --tty Terminal only mode. Does not run an X or Wayland server.
--xdummy Invisible X server using dummy video driver.
--xvfb Invisible X server using Xvfb.
--xdummy and --xvfb can be used for custom VNC access.
Output of environment variables on stdout. (--showenv)
Along with option --gpu an invisible setup with Weston,
Xwayland and xdotool is used (instead of Xdummy or Xvfb).
-X, --xwayland Blanc Xwayland, needs a running Wayland compositor.
--xwin X server to run in Cygwin/X on MS Windows.
Wayland instead of X:
-W, --wayland Automatically set up a Wayland environment.
Chooses one of following options and regards --desktop.
-T, --weston Weston without X for pure Wayland applications.
Runs in X, in Wayland or from console.
-K, --kwin KWin without X for pure Wayland applications.
Runs in X, in Wayland or from console.
-H, --hostwayland Share host Wayland without X for pure Wayland apps.
X and Wayland appearance options:
--border [=COLOR] Draw a colored border in windows of --xpra[-xwayland].
Argument COLOR can be e.g. 'orange' or '#F00'. Thickness
can be specified, too, e.g. 'red,3'. Default: 'blue,1'
--dpi N dpi value (dots per inch) to submit to X clients.
Influences font size of some applications.
-f, --fullscreen Run in fullscreen mode.
--output-count N Multiple virtual monitors for Weston, KWin or Xephyr.
--rotate N Rotate display (--xorg, --weston and --weston-xwayland)
Allowed values: 0, 90, 180, 270, flipped, flipped-90,
flipped-180, flipped-270. (flipped means mirrored)
--scale N Scale/zoom factor N for xpra, Xorg or Weston.
Allowed for --xpra, --xorg --xpra-xwayland: 0.25...8.0.
Allowed for --weston and --weston-xwayland: 1...9.
(Mismatching font sizes can be adjusted with --dpi).
Odd resolutions with --xorg might need --scale=1.
--size WxH Screen size of new X server (e.g. 800x600).
-w, --wm [=ARG] Provide a window manager to container applications.
If available, image x11docker/openbox will be used.
Otherwise x11docker looks for a host window manager.
Possible ARG:
host: Enforce autodetection of a host window manager.
COMMAND: COMMAND can be a desired host window manager.
IMAGE: IMAGE can be a local docker image with a WM.
none: Run without a window manager. Same as --desktop.
-F, --xfishtank Show fish tank on new X server.
X and Wayland special configuration:
--clean-xhost Disable xhost access policies on host display.
--display N Use display number N for new X server.
--keymap LAYOUT Set keyboard layout for new X server, e.g. de, us, ru.
For possible LAYOUT look at /usr/share/X11/xkb/symbols.
--no-auth Allow access to X for everyone. Security risk!
--vt N Use vt / tty N (regarded by --xorg only).
--westonini FILE Custom weston.ini for --weston and --weston-xwayland.
--xhost STR Set \"xhost STR\" on new X server (see 'man xhost').
(Use with care. '--xhost +' allows access for everyone).
--xoverip Connect to X over TCP network. For special setups only.
Only supported by a subset of X server options.
--xtest [=yes|no] Enable or disable X extension XTEST. Default is yes for
--xpra, --xvfb and --xdummy, no for other X servers.
Needed to allow custom access with xpra.
Container user settings:
--group-add GROUP Add container user to group GROUP.
--hostuser USER Run X (and container user) as user USER. Default is
result of \$(logname). (x11docker must run as root).
--sudouser Allow su and sudo for container user. Use with care,
severe reduction of default x11docker security!
--user N Create container user N (N=name or N=uid). Default:
same as host user. N can also be an unknown user id.
You can specify a group id with N being 'user:gid'.
Special case: --user=RETAIN keeps image user settings.
Container capabilities:
In most setups x11docker sets --cap-drop=ALL --security-opt=no-new-privileges
and shows warnings if doing otherwise.
Custom capabilities can be added with --cap-add=CAP after --
--cap-default Allow default docker container capabilities.
Includes --newprivileges=yes.
--hostipc Sets docker option --ipc=host. Disables IPC namespacing.
Severe reduction of container isolation! Shares
host interprocess communication and shared memory.
Allows MIT-SHM extension of X servers.
--limit [=FACTOR] Limit CPU and RAM usage of container to
currently free RAM x FACTOR and available CPUs x FACTOR.
Allowed range is 0 < FACTOR <= 1.
Default for --limit without argument FACTOR: 0.5
--newprivileges [=yes|no|auto] Set or unset docker run option
--security-opt=no-new-privileges. Default with no
argument is 'yes'. Default for most cases is 'no'.
Container init system, elogind and DBus daemon:
--dbus [=system] Run DBus user session daemon for container command.
With argument 'system' also run a DBus system daemon.
(To run a DBus system daemon rather use one of
--init=systemd|openrc|runit|sysvinit )
--hostdbus Connect to DBus user session from host.
--init [=INITSYSTEM] Run an init system as PID 1 in container. Solves the
zombie reaping issue. INITSYSTEM can be:
tini: Default. Mostly present as docker-init on host.
none: No init system, container command will be PID 1.
Others: systemd, sysvinit, runit, openrc, s6-overlay.
--sharecgroup Share /sys/fs/cgroup. Allows elogind in container if
used with one of --init=openrc|runit|sysvinit
Container special configuration:
--env VAR=value Set custom environment variable VAR=value
--name NAME Specify container name NAME.
--no-entrypoint Disable ENTRYPOINT in image to allow other commands, too
--runtime RUNTIME Specify docker runtime. Known by x11docker:
runc: Docker default runtime.
crun: Fast replacement for runc written in C.
nvidia: Runtime for nvidia/nvidia-docker images.
kata-runtime: Runtime using a QEMU VM.
--shell SHELL Set preferred user shell. Example: --shell=/bin/zsh
--stdin Forward stdin of x11docker to container command.
--workdir DIR Set working directory DIR.
Additional commands: (You might need to move them to background with 'CMD &'.)
--runasroot CMD Run command CMD as root in container.
Caution: Runs with --privileged host access.
--runasuser CMD Run command CMD with user privileges in container
before running image command.
--runfromhost CMD Run host command CMD on new X server.
Miscellaneous:
--cachebasedir DIR Custom base folder for cache files.
--homebasedir DIR Custom base folder for option --home.
--enforce-i Run x11docker in interactive bash mode to allow tty
access. Can help to run weston-launch on special systems.
--fallback [yes|no] Allow or deny fallbacks if a chosen option cannot
be fulfilled. By default fallbacks are allowed.
--launcher Create application launcher with current options
on desktop and exit. You can get a menu entry moving
the created .desktop file to ~/.local/share/applications
--mobyvm Use MobyVM (for WSL2 only that defaults to linux Docker).
--preset FILE Read a set of predefined options stored in file FILE.
Useful to shortcut often used option combinations.
FILE is searched in directory /etc/x11docker/preset,
or in directory ~/.config/x11docker/preset or absolute.
Multiple lines in FILE are allowed.
Comment lines must begin with #
--pull [=ask|yes|no|always] Behaviour if image is missing on host.
ask: Ask in terminal, timeout after 60s (default).
yes: Allow docker pull (default for --pull without arg).
no: Do not run or ask for 'docker pull'
always: Always run 'docker pull'. Download only if
newer image is available. Allows sort of auto-update.
--pw FRONTEND Choose frontend for password prompt. Possible FRONTEND:
su sudo gksu gksudo lxsu lxsudo kdesu kdesudo
pkexec beesu none
Output of parseable information on stdout:
Get output e.g. with: read xenv < <(x11docker --showenv x11docker/check)
--showenv Print new \$DISPLAY, \$XAUTHORITY and \$WAYLAND_DISPLAY.
--showid Print container ID.
--showinfofile Print path to internal x11docker info storage file.
--showpid1 Print host PID of container PID 1.
Verbosity options:
-D, --debug Debug mode: Show some less verbose debug output
and enable rigorous error control.
-q, --quiet Suppress x11docker terminal messages.
-v, --verbose Be verbose. Output of x11docker.log on stderr.
-V Be verbose with colored output.
Installation options (need root permissions), license and cleanup:
--install Install x11docker and x11docker-gui from current folder.
Useful to install from an extracted zip file.
--update Download and install latest release from github.
--update-master Download and install latest master version from github.
--remove Remove x11docker from your system. Includes --cleanup.
Preserves ~/.local/share/x11docker from option --home.
--license Show license of x11docker (MIT) and exit.
--cleanup Clean up orphaned containers and cache files.
Terminates currently running x11docker containers, too.
Exit codes:
0: Success
64: x11docker error
130: Terminated by ctrl-c
other: Exit code of command in container
x11docker version: $Version
Please report issues and get help at: https://github.com/mviereck/x11docker
"
}
license() { # --license: show license (MIT)
echo 'MIT License
Copyright (c) 2015, 2016, 2017, 2018, 2019, 2020, 2021 Martin Viereck
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.'
}
#### messages
alertbox() { # X alert box with title $1 and message $2
local Title Message
Title="${1:-}"
Message="${2:-}"
Message="$(echo "$Message" | LANG=C sed "s/[\x80-\xFF]//g" | fold -w120 )" # remove UTF-8 special chars; line folding at 120 chars
# try some tools to show alert message. If all tools fail, return 1
command -v xmessage >/dev/null && [ -n "${DISPLAY:-}" ] && {
echo "$Title
$Message" | xmessage -file - -default okay ||:
} || {
command -v gxmessage >/dev/null && [ -n "${DISPLAY:-}" ] && {
echo "$Title
$Message" | gxmessage -file - -default okay ||:
}
} || {
command -v zenity >/dev/null && [ -n "${DISPLAY:-}" ] && {
zenity --error --no-markup --ellipsize --title="$Title" --text="$Message" 2>/dev/null ||:
}
} || {
command -v yad >/dev/null && [ -n "${DISPLAY:-}" ] && {
yad --image "dialog-error" --title "$Title" --button=gtk-ok:0 --text "$(echo "$Message" | sed 's/\\/\\\\/g')" --fixed 2>/dev/null ||:
}
} || {
command -v kaptain >/dev/null && [ -n "${DISPLAY:-}" ] && {
echo 'start "'$Title'" -> message @close=" cancel" ;
message "'$(echo "$Message" | sed 's/\\/\\\\\\/g' | sed 's/"/\\"/g' | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g' )'" -> @fill ;' | kaptain ||:
}
} || {
command -v kdialog >/dev/null && [ -n "${DISPLAY:-}" ] && {
kdialog --title "$Title" --error "$(echo "$Message" | sed 's/\\/\\\\/g' )" 2>/dev/null ||:
}
} || {
command -v xterm >/dev/null && [ -n "${DISPLAY:-}" ] && {
xterm -title "$Title" -e "echo '$(echo "$Message" | sed "s/'/\"/g")' ; read -n1" ||:
}
} || {
[ -n "$Passwordterminal" ] && [ "$Passwordterminal" != "eval" ] && [ -e "$Cachefolder" ] && {
mkfile $Cachefolder/message
echo "#! /usr/bin/env bash
echo '$Title
$Message
(Press any key to close window)'
read -n1
:
" >> $Cachefolder/message
$Passwordterminal /usr/bin/env bash $Cachefolder/message
}
} || {
notify-send "$Title:
$Message" 2>/dev/null
} || {
warning "Could not display message on X:
$Message"
return 1
}
return 0
}
debugnote() { # show debug output $*
[ "$Debugmode" = "yes" ] && [ "$Verbose" = "no" ] && echo "${Colblue}DEBUGNOTE[$(timestamp)]:${Colnorm} $*" >&${FDstderr}
logentry "DEBUGNOTE[$(timestamp)]: $*"
return 0
}
error() { # show error message and exit
local Message
Message="$*
Type 'x11docker --help' for usage information
Debug options: '--verbose' (full log) or '--debug' (log excerpt).
Logfile will be: $Logfilebackup
Please report issues at https://github.com/mviereck/x11docker"
Message="$(rmcr <<< "$Message")"
# output to terminal
[ "$Verbose" = "no" ] && echo -e "
${Colredbg}x11docker ERROR:${Colnorm} $Message
" >&2
# output to logfile
logentry "x11docker ERROR: $Message
"
saygoodbye error
storeinfo test error && waitfortheend
storeinfo error=64
# output to X dialogbox if not running in terminal
[ "$Runsinterminal" = "no" ] && [ "$Silent" = "no" ] && export ${Hostxenv:-DISPLAY} && alertbox "x11docker ERROR" "$Message" &
finish
}
logentry() { # write into logfile
[ -e "$Logfile" ] && {
[ -n "$Logmessages" ] && echo "$Logmessages" >>$Messagelogfile 2>/dev/null && Logmessages=""
echo "$*" >>$Messagelogfile 2>/dev/null
:
} || Logmessages="$Logmessages
$*"
}
note() { # show notice messages
[ "$Verbose" = "no" ] && echo "${Colgreen}x11docker note:${Colnorm} $*
" >&${FDstderr}
logentry "x11docker note: $*
"
}
traperror() { # trap ERR: --debug: Output for 'set -o errtrace'
debugnote "traperror: Command at Line ${2:-} returned with error code ${1:-}:
${4:-}
${3:-} - ${5:-}"
storeinfo error=64
saygoodbye traperror
}
verbose() { # show verbose messages
# only logfile notes here, terminal output is done with tail in setup_verbosity()
logentry "x11docker[$(timestamp)]: $*
"
}
warning() { # show warning messages
[ "$Verbose" = "no" ] && echo "${Colyellow}x11docker WARNING:${Colnorm} $*
" >&${FDstderr}
logentry "x11docker WARNING: $*
"
}
watchmessagefifo() { # watch for messages coming from container or dockerrc
# message in fifo must end with :$Messagetype
local Line= Message= Messagetype=
trap '' SIGINT
while [ -e "$Cachefolder" ]; do
IFS= read -r Line <&${FDmessage} ||:
[ "$Line" ] || sleep 2 # sleep for MSYS2/CYGWIN workaround
[ "$Line" ] && Message="$Message
$Line"
grep -q -E ":WARNING|:NOTE|:DEBUGNOTE|:VERBOSE|:ERROR|:STDOUT" <<< "$Line" && {
Messagetype=":$(echo $Line | rev | cut -d: -f1 | rev)"
Message="${Message%$Messagetype }"
Message="$(tail -n +2 <<< "$Message")" # remove leading newline
case "$Messagetype" in
:WARNING) warning "$Message" ;;
:NOTE) note "$Message" ;;
:DEBUGNOTE) debugnote "$Message" ;;
:ERROR) error "$Message" ;;
:VERBOSE) [ "-d " = "$(cut -c1-3 <<<"$Message" | head -n1)" ] && debugnote "$(tail -c +4 <<< "$Message")" || verbose "$Message" ;;
:STDOUT) echo "$Message" ;;
esac
Message=
Messagetype=
}
done
}
#### exit
finish() { # trap EXIT routine to clean up background processes and cache
local Pid Name Zeit Exitcode Pid1pid= Dockerlogspid= Dockerstopshellpid= Wmcontainerpid1= Watchmessagefifopid= i
# do not finish() in subshell, just give signal to all other processes and terminate subshell
[ "$$" = "$BASHPID" ] || {
saygoodbye finish-subshell
exit 0
}
debugnote "Terminating x11docker."
saygoodbye "finish"
trap - EXIT
trap - ERR
trap - SIGINT
# --pw=sudo: no password prompt here, rather fail ### FIXME
[ "$Sudo" ] && {
sudo -n echo 2>/dev/null && Sudo="sudo -n" || Sudo=""
}
while read -r Line ; do
Pid="$(echo $Line | awk '{print $1}')"
Name="$(echo $Line | awk '{print $2}')"
debugnote "finish(): Checking pid $Pid ($Name): $(pspid $Pid || echo '(already gone)')"
checkpid $Pid && {
case $Name in
watchmessagefifo)
Watchmessagefifopid="$Pid"
;;
dockerstopshell)
Dockerstopshellpid="$Pid"
;;
dockerlogs)
Dockerlogspid=$Pid
#[ "$Winsubsystem" ] && Dockerlogspid=""
;;
containerpid1)
Pid1pid="$Pid"
#[ "$Winsubsystem" ] && Pid1pid=""
termpid "$Pid1pid" "$Name" || Debugmode="yes"
# Give container time for graceful shutdown
for Count in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0; do
checkpid $Pid1pid || break
mysleep $(awk "BEGIN { print $Count * 0.1 }")
debugnote "finish(): Waiting for container PID 1: $Pid1pid to terminate."
done
;;
wmcontainerpid1)
Wmcontainerpid1="$Pid"
#[ "$Winsubsystem" ] && Wmcontainerpid1=""
termpid "$Wmcontainerpid1" "$Name"
;;
*)
termpid "$Pid" "$Name"
;;
esac
}
done < <(tac "$Storepidfile" 2>/dev/null)
# --pulseaudio: unload module
Pulseaudiomoduleid="$(storeinfo dump pulseaudiomoduleid)"
[ "$Pulseaudiomoduleid" ] && pactl unload-module "$Pulseaudiomoduleid"
# Check if container is still running -> docker stop
[ "$X11dockermode" = "run" ] && containerisrunning && {
Debugmode="yes"
debugnote "finish(): Container still running. Executing 'docker stop'.
Will wait up to 15 seconds for docker to finish."
case $Mobyvm in
no) echo "stop" >> "$Dockerstopsignalfifo" ;;
yes) $Dockerexe stop $Containername >>$Containerlogfile 2>&1 ;;
esac
Zeit="$(date +%s)"
while :; do
containerisrunning || break
debugnote "finish(): Waiting for container to terminate ..."
sleep 1
[ 15 -lt $(($(date +%s) - $Zeit)) ] && break
done
containerisrunning && {
Exitcode="64"
debugnote "finish(): Container did not terminate as it should.
Will not clean cache to avoid file permission issues.
You can remove the new container with command:
docker rm -f $Containername
Afterwards, remove cache files with:
rm -R $Cachefolder
or let x11docker do the cleanup work for you:
x11docker --cleanup"
Preservecachefiles="yes"
} || debugnote "finish(): Container terminated successfully"
}
# remove container
[ "$Preservecachefiles" = "no" ] && [ "$Containername" ] && {
debugnote "Removing container $Containername
$($Dockerexe rm -f "$Containername" 2>&1)"
}
# Check if 'docker logs' is still running
[ "$FDdockerstop" ] && {
checkpid $Dockerlogspid && echo "stop" >&${FDdockerstop}
# Check if window manager container is still running
checkpid $Wmcontainerpid1 && echo "stop" >&${FDdockerstop}
# Terminate watching subshell in dockerrc
[ -e "$Dockerstopsignalfifo" ] && echo "exit" >&${FDdockerstop}
checkpid $Dockerstopshellpid && sleep 1
}
# Stop watching for messages, check others again
while read -r Line ; do
Pid="$(echo $Line | awk '{print $1}')"
Name="$(echo $Line | awk '{print $2}')"
checkpid $Pid && termpid "$Pid" "$Name"
checkpid $Pid && {
# should never happen
warning "Failed to terminate pid $Pid ($Name): $(pspid $Pid ||:)"
storeinfo error=64
}
done < <(tac "$Storepidfile" 2>/dev/null)
Exitcode=$(storeinfo dump error)
Exitcode="${Exitcode:-0}"
debugnote "x11docker exit code: $Exitcode"
storeinfo test cmdexitcode && {
Exitcode=$(storeinfo dump cmdexitcode)
debugnote "CMD exit code: $Exitcode"
}
# backup of logfile in $Cachebasefolder
[ -e "$Logfile" ] && {
[ "$Verbose" = "yes" ] && sleep 1
unpriv "cp '$Logfile' '$Logfilebackup'"
case $Winsubsystem in
WSL1|WSL2)
[ "$Mobyvm" = "yes" ] && unpriv "cp -T '$Logfilebackup' '$Hostuserhome/.cache/x11docker/x11docker.log'"
;;
esac
#unpriv "rmcr '$Logfilebackup'"
}
# close file descriptors
mysleep 0.2
for Descriptor in ${FDcmdstdin} ${FDdockerstop} ${FDmessage} ${FDstderr} ${FDtimetosaygoodbye} ${FDwatchpid} ; do
exec {Descriptor}>&-
done
# remove cache files
[ "$Preservecachefiles" = "no" ] && grep -q cache <<<$Cachefolder && grep -q x11docker <<<$Cachefolder && [ "x11docker" != "$(basename "$Cachefolder")" ] && unpriv "rm -f -R '$Cachefolder'"
case $Runssourced in
yes) return $Exitcode ;;
*) exit $Exitcode ;;
esac
}
finish_sigint() { # trap SIGINT to activate debug mode on finish()
local Pid1pid
Debugmode="yes"
debugnote "Received SIGINT"
storeinfo error=130
finish
}
saygoodbye() { # create file signaling watching processes to terminate
debugnote "time to say goodbye ($*)"
[ -e "$Timetosaygoodbyefile" ] && echo timetosaygoodbye >> $Timetosaygoodbyefile
[ -e "$Timetosaygoodbyefifo" ] && echo timetosaygoodbye >> $Timetosaygoodbyefifo
}
#### watching processes
checkpid() { # check if PID $1 is active
#ps -p ${1:-} >/dev/null 2>&1
[ -e "/proc/${1:-NONSENSE}" ]
}
containerisrunning() { # check if container is running
storeinfo test containerid || return 1
case $Mobyvm in
no) checkpid "$(storeinfo dump pid1pid)" ;;
yes) $Dockerexe inspect "$(storeinfo dump containerid)" >/dev/null 2>&1 ;;
esac
}
pspid() { # ps -p $1 --no-headers
# On some systems ps does not have option --no-headers.
# On some systems (busybox) ps -p is not supported ### FIXME
# return 1 if not found
LC_ALL=C ps -p "${1:-}" 2>/dev/null | grep -v 'TIME'
}
rocknroll() { # check whether x11docker session is still running
[ -s "$Timetosaygoodbyefile" ] && return 1
[ -e "$Timetosaygoodbyefile" ] || return 1
return 0
}
setonwatchpidlist() { # add PID $1 to watchpidlist()
debugnote "watchpidlist(): Setting pid ${1:-} on watchlist: ${2:-}"
echo "${1:-}" >>$Watchpidfifo
# add to list of background processes
grep -q CONTAINER <<< "${1:-}" || storepid "${1:-}" "${2:-}"
}
storepid() { # store pid $1 and name $2 of process in file $Storepidfile.
# Store pid and process name of background processes in a file
# Used in finish() to clean up background processes
# Store:
# $1 Pid
# $2 codename
# Test for stored pid or codename:
# $1 test
# $2 pid or codename
# Dump stored pid:
# $1 dump
# $2 codename
case "${1:-}" in
dump) grep -w "${2:-}" "$Storepidfile" | cut -d' ' -f1 ;;
test) grep -q -w "${2:-}" "$Storepidfile" ;;
*)
echo "${1:-NOPID}" "${2:-NONAME}" >> "$Storepidfile"
debugnote "storepid(): Stored pid '${1:-}' of '${2:-}': $(pspid ${1:-} ||:)"
;;
esac
}
termpid() { # kill PID $1 with codename $2
# TERM
debugnote "termpid(): Terminating ${1:-} (${2:-}): $(pspid ${1:-} ||:)"
checkpid "${1:-}" && {
kill ${1:-} 2>/dev/null
:
} || return 0
mysleep 0.1
checkpid "${1:-}" && mysleep 0.4 || return 0
# KILL
debugnote "termpid(): Killing ${1:-} (${2:-}): $(pspid ${1:-} ||:)"
checkpid "${1:-}" && kill -s KILL ${1:-} 2>/dev/null
mysleep 0.2
checkpid "${1:-}" && {
note "Failed to terminate ${1:-} (${2:-}): $(ps -u -p ${1:-} 2>/dev/null | tail -n1)"
return 1
}
return 0
}
waitfortheend() { # wait for end of x11docker session
# signal is byte in $Timetosaygoodbyefifo
# decent read to wait for signal to terminate
case $Usemkfifo in
yes)
while rocknroll; do
bash -c "read -n1 <${FDtimetosaygoodbye}" && saygoodbye timetosaygoodbyefifo || sleep 1
done
;;
no) # Reading from fifo fails on Windows, workaround
while rocknroll; do
sleep 2
done
;;
esac
return 0
}
watchpidlist() { # watch list of important pids
# terminate x11docker if a PID in $Watchpidlist terminates
# serves mainly watching X server, Wayland compositor, container and hostexe
# echo PIDs to watch into >{FDwatchpid} (setonwatchpidlist())
local Pid= Containername= Line= Watchpidlist=
trap '' SIGINT
while rocknroll; do
# check for new Pid once a second
read -t1 Pid <&${FDwatchpid} ||:
[ "$Usemkfifo" = "no" ] && sleep 2 # read does not wait if not a fifo
# Got new pid
[ "$Pid" ] && {
[ "${Pid:0:9}" = "CONTAINER" ] && {
# Workaround for MS Windows where the pid cannot be watched
Containername="${Pid#CONTAINER}"
debugnote "watchpidlist(): Watching Container: $Containername"
} || {
Watchpidlist="$Watchpidlist $Pid"
debugnote "watchpidlist(): Watching pids:
$(for Line in $Watchpidlist; do pspid "$Line" || echo "(pid $Line not found)" ; done)"
}
}
# check all stored pids
for Pid in $Watchpidlist; do
[ -e /proc/$Pid ] || {
debugnote "watchpidlist(): PID $Pid has terminated"
saygoodbye "watchpidlist $Pid"
}
done
# Container PID not watchable in MSYS2/Cygwin/WSL11.
[ "$Containername" ] && {
$Dockerexe inspect $Containername >/dev/null 2>&1 || {
debugnote "watchpidlist(): Container $Containername has terminated"
saygoodbye "watchpidlist $Containername"
}
}
done
saygoodbye "watchpidlist"
}
#### more or less general routines
askyesno() { # ask Yes/no question. Default 'yes' for ENTER, timeout with 'no' after 60s
local Choice
read -t60 -n1 -p "(timeout after 60s assuming no) [Y|n]" Choice
[ "$?" = '0' ] && {
[[ "$Choice" == [YyJj]* ]] || [ -z "$Choice" ] && return 0
}
return 1
}
check_envvar() { # allow only chars in string $1 that can be exspected in environment variables
# Allows only chars in "a-zA-Z0-9_:/.,@=-"
# Option -w allows whitespace, too. Can be needed for PATH.
# Char * as in LS_COLORS is not allowed to avoid abuse.
# Replaces forbidden chars with X and returns 1
# Returns 0 if no change occured.
# Echoes result.
local Newvar Space=
case "${1:-}" in
-w) Space=" " ; shift ;;
esac
Newvar="$(printf %s "${1:-}" | LC_ALL=C tr -c "a-zA-Z0-9_:/.,@=${Space}-" "X" )"
printf %s "$Newvar"
printf "\n"
[ "$Newvar" = "${1:-}" ] && return 0
debugnote "check_envvar(): Input string has been changed. Result:
$Newvar"
return 1
}
check_parent_sshd() { # check whether pid $1 runs in SSH session
local Wanted_pid="${1:-}" Process_line
local Return
ps -p 1 >/dev/null 2>&1 || {
debugnote "check_parent_sshd(): Failed to check for sshd. ps -p not supported."
return 1
}
while [ $Wanted_pid -ne 1 ] ; do
Process_line="$(ps -f -p "$Wanted_pid"| tail -n1)"
Wanted_pid="$(echo $Process_line| awk '{print $3}')"
[[ $Process_line =~ sshd ]] && Return=0
[ "$Return" ] && break
done
return ${Return:-1}
}
download() { # download file at URL $1 and store it in file $2
# Uses wget or curl. If both are missing, returns 1.
# With no arguments it checks for curl/wget without downloading.
# Download follows redirects.
local Downloader=
command -v wget >/dev/null && Downloader="wget"
command -v curl >/dev/null && Downloader="curl"
[ "$Downloader" ] || return 1
[ "${1:-}" ] || return 0
case $Downloader in
wget) wget "${1:-}" -O "${2:-}" ;;
curl) curl -L "${1:-}" --output "${2:-}" ;;
esac
}
escapestring() { # escape special chars of $1
# escape all characters except those described in [^a-zA-Z0-9,._+@=:/-]
echo "${1:-}" | LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@=:/-]/\\&/g; '
}
getrandomnumber() { # get random number
# chosen by fair dice roll
# guaranteed to be random
echo "4"
}
isnum() { # check if $1 is a number
[ "1" = "$(awk -v a="${1:-}" 'BEGIN {print (a == a + 0)}')" ]
}
makecookie() { # bake a cookie
mcookie 2>/dev/null || echo $RANDOM$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM | cut -b1-32
}
mysleep() { # catch cases where sleep only supports integer
sleep "${1:-1}" 2>/dev/null || sleep 1
}
storeinfo() { # store some information for later use
# store and provide pieces of information
# replace entry if codeword is already present
# Store as codeword=string:
# $1 codeword=string
# Dump stored string:
# $1 dump
# #2 codeword
# Drop stored string:
# $1 drop
# #2 codeword
# Test for codeword: (return 1 if not found)
# $1 test
# $2 codeword
#
# note: sed -i causes file permission issues if called in container in Cygwin, compare ticket #187
# chmod 666 for $Sharefolder could probably fix that. (FIXME)
#
[ -e "$Storeinfofile" ] || return 1
case "${1:-}" in
dump) grep "^${2:-}=" $Storeinfofile | sed "s/^${2:-}=//" ;; # dump entry
drop) sed -i "/^${2:-}=/d" $Storeinfofile ;; # drop entry
test) grep -q "^${2:-}=" $Storeinfofile ;; # test for entry
*) # store entry
debugnote "storeinfo(): ${1:-}"
grep -q "^$(echo "${1:-}" | cut -d= -f1)=" $Storeinfofile && {
sed -i "/^$(echo "${1:-}" | cut -d= -f1)=/d" $Storeinfofile # drop possible old entry
}
echo "${1:-}" >> $Storeinfofile
;;
esac
}
rmcr() { # remove carriage return to translate DOS/Windows newlines into UNIX newlines
# convert stdin if $1 is empty. Otherwise convert file $1.
case "${1:-}" in
"") sed "s/$(printf "\r")//g" ;;
*) sed -i "s/$(printf "\r")//g" "${1:-}" ;;
esac
}
timestamp() { # print HH:MM:SS,NNN
date +%T,%N | cut -c1-12
}
unspecialstring() { # replace special chars of $1 with -
# Replace all characters except those described in "a-zA-Z0-9_" with a '-'.
# Replace newlines, too.
# Remove leading and trailing '-'
# Avoid double '--'
# Return empty string if only special chars are given.
printf %s "${1:-}" | LC_ALL=C tr -cs "a-zA-Z0-9_" "-" | sed -e 's/^-// ; s/-$//'
}
verlt() { # version number check $1 less than $2
[ "${1:-}" = "${2:-}" ] && return 1 || { verlte "${1:-}" "${2:-}" && return 0 || return 1 ; }
}
verlte() { # version number check $1 less than or equal $2
[ "${1:-}" = "$(echo -e "${1:-}\n${2:-}" | sort -V | head -n1)" ] && return 0 || return 1
}
wincmd() { # execute a command on MS Windows with cmd.exe
MSYS2_ARG_CONV_EXCL='*' cmd.exe /C "${@//&/^&}" | rmcr
}
#### file routines
convertpath() { # convert unix and windows pathes
# $1: Mode:
# windows echo Windows path - result: c:/path
# unix echo unix path - result: /c/path
# subsystem echo path within subsystem - result: /cygdrive/c/path or /path or /mnt/c/path
# volume echo --volume compatible syntax - result: 'unixpath':'containerpath':rw (or ":ro")
# container echo path of volume in container - result: /path
# share echo path of $Sharefolder/file in container - result: /containerpath
# $2: Path to convert. Arbitrary syntax, can be C:/path, /c/path, /cygdrive/c/path, /path
# Can have suffix :rw or :ro. If none is given, return with :rw
# $3: Optional for mode volume: containerpath
local Mode Path Drive= Readwritemode
Mode="${1:-}"
Path="${2:-}"
# check path for suffix :rw or :ro
Readwritemode="$(echo "$Path" | rev | cut -c1-3 | rev)"
[ "$(cut -c1 <<< "$Readwritemode")" = ":" ] && {
Path="$(echo "$Path" | rev | cut -c4- | rev)"
} || Readwritemode=":rw"
# replace ~ with HOME
Path="$(sed s%"~"%"${Hostuserhome:-${HOME:-}}"% <<< "$Path")"
# share: Replace $Sharefolder with $Sharefoldercontainer
[ "$Mode" = "share" ] && {
[ -z "$Path" ] && echo "" && return 0
case $X11dockermode in
run) echo "${Sharefoldercontainer}${Path#$Sharefolder}" ;;
exe) echo "$Path" ;;
esac
return 0
}
# replace \ with /
Path="$(tr '\\' '/' <<< "$Path")"
# remove possible already given mountpoint
Path="${Path#$Winsubmount}"
# Given format is /c/
[ "$(cut -c1,3 <<< "$Path")" = "//" ] && {
Drive="$(cut -c2 <<< "$Path")"
Path="$(cut -c3- <<< "$Path")"
}
# Given format is C:/
[ "$(cut -c2 <<< "$Path")" = ":" ] && {
Drive="$(cut -c1 <<< "$Path")"
Path="$(cut -c3- <<< "$Path")"
}
# change C to c
Drive="${Drive,}"
# docker volume
[ "${Path:0:1}" = "/" ] || {
case $Mode in
unix|subsystem|windows) echo "$Path" ; debugnote "convertpath() $Mode: Docker volumes do not have a specified path on host: $Path" ;;
volume) echo "'$Path':'${3:-/$Path}'$Readwritemode" ;;
container) echo "${3:-/$Path}" ;;
esac
return 0
}
# not on Windows
[ -z "$Winsubsystem" ] && {
case $Mode in
unix|subsystem) echo "$Path" ;;
windows) warning "convertpath(): Nonsense path conversion $Mode: $Path" ; return 1 ;;
volume) echo "'$Path':'${3:-$Path}'$Readwritemode" ;;
container) echo "${3:-$Path}" ;;
esac
return 0
}
case $Winsubsystem in
WSL1)
[ -z "$Drive" ] && case $Mode in
windows|unix|volume)
debugnote "convertpath(): Request of WSL path: $Path"
grep -q "$Cachefolder" <<< "$Path" || {
[ "$Readwritemode" = ":rw" ] && warning "Request of Windows path to path within WSL:
$Path
Write access from Windows host to WSL files can damage the WSL file system.
Read-only access is ok.
Option --share: You can add :ro to the path to allow read-only access.
Example: --share='$Path:ro'"
}
;;
esac
;;
esac
case $Drive in
"") # Path points into subsystem
Path="${Path#"$Winsubpath"}"
Drive="$(cut -c2 <<<"$Winsubpath")"
case $Mode in
windows) echo "${Drive^}:$(cut -c3- <<<$Winsubpath)$Path" ;;
unix) echo "$Winsubpath$Path" ;;
subsystem) echo "$Path" ;;
volume)
case $Mobyvm in
no) echo "'$Path':'${3:-$Path}'$Readwritemode" ;;
yes) echo "'$Winsubpath$Path':'${3:-$Path}'$Readwritemode" ;;
esac
;;
container) echo "${3:-$Path}" ;;
esac
;;
*) # Path outside of subsystem
case $Mode in
windows) echo "${Drive^}:$Path" ;;
unix) echo "/$Drive$Path" ;;
subsystem) echo "$Winsubmount/$Drive$Path" ;;
volume) echo "'/$Drive$Path':'${3:-/$Drive$Path}'$Readwritemode" ;;
container) echo "${3:-/$Drive$Path}" ;;
esac
;;
esac
return 0
}
getwslpath() { # get path to currently running WSL system
# Fork from https://github.com/Microsoft/WSL/issues/2578#issuecomment-354010141
local RUN_ID= BASE_PATH=
RUN_ID="/tmp/$(makecookie)"
# Mark our filesystem with a temporary file having an unique name.
touch "${RUN_ID}"
powershell.exe -Command '(Get-ChildItem HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss | ForEach-Object {Get-ItemProperty $_.PSPath}).BasePath.replace(":", "").replace("\", "/")' | while IFS= read -r BASEPATH; do
# Remove trailing whitespaces.
BASEPATH="${BASEPATH%"${BASEPATH##*[![:space:]]}"}"
# Build the path on WSL.
BASEPATH="/mnt/${BASEPATH,}/rootfs"
# Current WSL instance doesn't have an access to its mount from within
# itself despite all others are available. That's the hacky way we're
# using to determine current instance.
#
# The second of part of the condition is a fallback for a case if our
# trick will stop working. For that we've created a temporary file with
# an unique name and now seeking it among all WLSs.
if ! ls "${BASEPATH}" > /dev/null 2>&1 || [ -f "${BASEPATH}${RUN_ID}" ]; then
echo "${BASEPATH}"
# You can create and simultaneously run multiple WSL instances, comment
# out the "break", run this script within each one and it'll return only
# single value.
break
fi
done
rm "${RUN_ID}"
return 0
}
mkfile() { # create file $1 owned by $Hostuser
:> "${1:-}" || return 1
chown $Hostuser "${1:-}" || return 1
chgrp $Hostusergid "${1:-}" || return 1
chmod 644 "${1:-}" || return 1
[ -n "${2:-}" ] && { chmod ${2:-} "${1:-}" || return 1 ; }
return 0
}
myrealpath() { # real path of possible symlink
command -v realpath >/dev/null && {
realpath "$@"
} || {
[ -h "$@" ] && warning "Could not check for symbolic links.
Please install 'realpath' (package 'coreutils'),
or provide real file path instead of symbolic link path.
Possible symbolic link: $@"
echo "$@" ### FIXME: Maybe workaround with ls
return 1
}
}
waitforlogentry() { # wait for entry $3 in logfile $2 of application $1
# $1 is the application we are waiting for to be ready
# $2 points to logfile
# $3 keyword to wait for
# $4 possible error keywords
# $5 time to wait in seconds or infinity. default: 60
local Startzeit Uhrzeit Dauer Count=0 Schlaf
local Errorkeys="${4:-}"
local Warten="${5:-60}"
local Error=
Startzeit="$(date +%s ||:)"
Startzeit="${Startzeit:-0}"
[ "$Warten" = "infinity" ] && Warten=32000
debugnote "waitforlogentry(): ${1:-}: Waiting for logentry \"${3:-}\" in $(basename ${2:-})"
while ! grep -q "${3:-}" <"${2:-}" ; do
Count="$(( $Count + 1 ))"
Uhrzeit="$(date +%s ||:)"
Uhrzeit="${Uhrzeit:-0}"
Dauer="$(( $Uhrzeit - $Startzeit ))"
Schlaf="$(( $Count / 10 ))"
[ "$Schlaf" = "0" ] && Schlaf="0.5"
mysleep "$Schlaf"
[ "$Dauer" -gt "10" ] && debugnote "waitforlogentry(): ${1:-}: Waiting since ${Dauer}s for log entry \"${3:-}\" in $(basename ${2:-})"
[ "$Dauer" -gt "$Warten" ] && error "waitforlogentry(): ${1:-}: Timeout waiting for entry \"${3:-}\" in $(basename ${2:-})
Last lines of $(basename ${2:-}):
$(tail "${2:-}")"
# grep -i -q -E 'xinit: giving up|unable to connect to X server|Connection refused|server error|Only console users are allowed|Failed to process Wayland|failed to create display|] fatal:' <"${2:-}" && \
[ "$Errorkeys" ] && grep -i -q -E "$Errorkeys" <"${2:-}" && \
error "waitforlogentry(): ${1:-}: Found error message in logfile.
Last lines of logfile $(basename ${2:-}):
$(tail "${2:-}")"
rocknroll || {
debugnote "waitforlogentry(): ${1:-}: Stopped waiting for ${3:-} in $(basename ${2:-}) due to terminating signal."
Error=1
break
}
done
[ "$Error" ] && return 1
debugnote "waitforlogentry(): ${1:-}: Found log entry \"${3:-}\" in $(basename ${2:-})."
return 0
}
writeaccess() { # check if useruid $1 has write access to folder $2
local dirVals= gMember= IFS=
IFS=$'\t' read -a dirVals < <(stat -Lc "%U %G %A" "${2:-}")
[ "$(id -u $dirVals)" == "${1:-}" ] && [ "${dirVals[2]:2:1}" == "w" ] && return 0
[ "${dirVals[2]:8:1}" == "w" ] && return 0
[ "${dirVals[2]:5:1}" == "w" ] && {
gMember="$(groups ${1:-} 2>/dev/null)"
[[ "${gMember[*]:2}" =~ ^(.* |)${dirVals[1]}( .*|)$ ]] && return 0
}
[ "w" = "$(getfacl -pn "${2:-}" | grep user:${1:-}: | rev | cut -c2)" ] && return 0 || return 1
}
#### special jobs of x11docker (not running X or docker)
cleanup() { # --cleanup : check for non-removed containers and left cache files
# Cleanes x11docker cache and removes running and stopped x11docker containers.
# Does not change --home folders.
local Orphanedcontainers= Orphanedfolders= Line=
note "x11docker will check for orphaned containers from earlier sessions.
This can happen if docker was not closed successfully.
x11docker will look for those containers and will clean up x11docker cache.
Caution: any currently running x11docker sessions will be terminated, too."
cd $Cachebasefolder || error "Could not cd to cache folder '$Cachebasefolder'."
grep -q .cache/x11docker <<<$Cachebasefolder && Orphanedfolders="$(find "$Cachebasefolder" -mindepth 1 -maxdepth 1 -type d | sed s%$Cachebasefolder/%% | grep -w -v x11docker-gui)"
# e X11DOCKER_LASTCLEANFOLDER may be set by x11docker-gui to spare its cache folder.
[ "${X11DOCKER_LASTCLEANFOLDER:-}" ] && Orphanedfolders="$(echo "$Orphanedfolders" | grep -v $X11DOCKER_LASTCLEANFOLDER)"
Orphanedcontainers="$($Dockerexe ps -a --filter name=x11docker_X | grep -v NAMES | rev | cut -d' ' -f1 | rev)"
Orphanedcontainers="$Orphanedcontainers $(find "$Cachebasefolder" -mindepth 2 -maxdepth 2 -type f -name 'container.id' -exec cat {} \;)"
Orphanedcontainers="$(env IFS='' echo $Orphanedcontainers)"
# check for double entrys name/id, check for already non-existing containers
for Line in $Orphanedcontainers; do
$Dockerexe inspect $Line -f '{{.Id}}' >/dev/null 2>/dev/null && {
echo $Line | grep -q x11docker_X && {
$Dockerexe inspect $Line -f '{{.Id}}'
Line="$($Dockerexe inspect $Line -f '{{.Id}}')"
Orphanedcontainers="$(sed s/$Line// <<< $Orphanedcontainers)"
} ||:
} || Orphanedcontainers="$(sed s/$Line// <<< $Orphanedcontainers)"
done
[ -z "$Orphanedcontainers$Orphanedfolders" ] && {
note "No orphaned containers or cache files found. good luck!"
} || {
note "Found orphaned containers:
$Orphanedcontainers"
note "Found orphaned folders in $Cachebasefolder:
$Orphanedfolders"
for Line in $Orphanedfolders ; do
[ -d "$Cachebasefolder/$Line/share" ] && [ ! -s "$Cachebasefolder/$Line/share/timetosaygoodbye" ] && {
note "Found possibly active container for cache dir $Line.
Will summon it to terminate itself."
echo timetosaygoodbye >> "$Cachebasefolder/$Line/share/timetosaygoodbye"
}
done
[ -n "$Orphanedfolders" ] && sleep 3
[ -n "$Orphanedcontainers" ] && {
note "Removing containers with: $Dockerexe rm -f $Orphanedcontainers"
bash -c "$Dockerexe rm -f $Orphanedcontainers" 2>&1
}
[ -n "$Orphanedfolders" ] && {
note "Removing cache files with: rm -R -f $Orphanedfolders"
rm -R -f $Orphanedfolders 2>&1
}
}
[ "${X11DOCKER_LASTCLEANFOLDER:-}" ] && {
echo timetosaygoodbye >>$X11DOCKER_LASTCLEANFOLDER/share/timetosaygoodbye
echo timetosaygoodbye >>$X11DOCKER_LASTCLEANFOLDER/share/timetosaygoodbye.fifo
sleep 2
}
Logfile=
note "Removing remaining files with: rm -Rf -v $Cachebasefolder/*"
rm -Rf -v $Cachebasefolder/*
note "Removing cache base folder with: rmdir -v $Cachebasefolder"
cd
[ "$(basename $Cachebasefolder)" = x11docker ] && rmdir -v $Cachebasefolder || warning "Did not succeed in removing cache folder
$Cachebasefolder
Please run 'x11docker --cleanup' as root."
$Dockerexe info >/dev/null 2>/dev/null || warning "Could not check for docker images.
Please run 'x11docker --cleanup' as root
to make sure that no orphaned containers are left."
note "Cleanup ready."
}
create_launcher() { # --launcher: create application launcher on desktop
local Name=
command -v xdg-desktop-icon >/dev/null || error "Command 'xdg-desktop-icon' not found.
x11docker needs it to place the new icon on your desktop.
Please install xdg-utils"
note "Will create a new application launcher icon on your desktop.
If you move the new file to:
$Hostuserhome/.local/share/applications
it will appear in your applications menu."
Name="$Codename"
[ "$Codename" = "xonly" ] && Name="$(echo $Xserver | tr -d '-')"
Name="${Name% }"
read -re -p "Please choose a name for your application launcher:
" -i "$Name" Name
[ -z "$Name" ] && return 1 ### FIXME: check for valid file name / invalid chars?
Parsedoptions_global="${Parsedoptions_global//--launcher/}"
Parsedoptions_global="${Parsedoptions_global//--starter/}"
mkfile "$Cachefolder/$Name.desktop"
{
echo "#!/usr/bin/xdg-open
[Desktop Entry]
# x11docker desktop file
Type=Application
Name=$Name
Exec=x11docker $Parsedoptions_global
Icon=x11docker
Comment=
Categories=System
Keywords=docker x11docker $(echo $Name | tr -c '[:alpha:][:digit:][:blank:]' ' ' )
"
case $(command -v x11docker) in
"")echo "TryExec=$0 $Parsedoptions_global" ;;
*) echo "TryExec=x11docker $Parsedoptions_global" ;;
esac
} >> "$Cachefolder/$Name.desktop"
unpriv "xdg-desktop-icon install --novendor '$Cachefolder/$Name.desktop'"
}
installer() { # --install, --update, --update-master, --remove: Installer for x11docker
# --install:
# - copies x11docker and x11docker-gui to /usr/bin
# - installs icon in /usr/share/icons
# - creates x11docker.desktop file in /usr/share/applications
# --update:
# - download and install latest release from github
# --update-master:
# - download and install latest master version from github
# --remove
# - remove installed files
local Key1= Key2= Oldversion= Newversion= Format=
export PATH="${PATH:-}:/usr/local/bin" # avoid bug on opensuse where root does not have this in $PATH. Will become obsolete as new default is /usr/bin
# Prepairing
case ${1:-} in
--install)
[ -f "./x11docker" ] || { error "File x11docker not found in current folder.
Try 'x11docker --update' instead." ; }
command -v x11docker > /dev/null && { warning "x11docker seems to be installed already.
Will overwrite existing installation.
Consider to use option '--update' or '--update-master' instead." ; }
;;
--update|--update-master)
grep -q x11docker <<< "$0" && {
Oldversion="$($0 --version)"
note "Current installed version: x11docker $Oldversion"
} || {
Oldversion=""
}
[ -d /tmp/x11docker-install ] && rm -R /tmp/x11docker-install
mkdir -p /tmp/x11docker-install && cd /tmp/x11docker-install || error "Could not create or cd to /tmp/x11docker-install."
download || error "Neither wget nor curl found. Need 'wget' or 'curl'for download.
Please install wget or curl."
command -v unzip >/dev/null && Format="zip"
command -v tar >/dev/null && Format="tar.gz"
[ "$Format" ] || error "Cannot extract archive. Please install 'unzip' or 'tar'."
case ${1:-} in
--update-master)
note "Downloading latest x11docker master version from github."
download "https://codeload.github.com/mviereck/x11docker/$Format/master" "x11docker-update.$Format" || error "Failed to download x11docker from github."
;;
--update)
download "https://raw.githubusercontent.com/mviereck/x11docker/master/CHANGELOG.md" "CHANGELOG.md" || error "Failed to download CHANGELOG.md from github."
Releaseversion="v$(cat CHANGELOG.md | grep "## \[" | grep -v 'Unreleased' | head -n1 | cut -d[ -f2 | cut -d] -f1)"
note "Downloading latest x11docker release $Releaseversion from github."
download "https://codeload.github.com/mviereck/x11docker/$Format/$Releaseversion" "x11docker-update.$Format" || error "Failed to download x11docker from github."
;;
esac
note "Extracting $Format archive."
case $Format in
zip) unzip "x11docker-update.$Format" ;;
tar.gz) tar xzf "x11docker-update.$Format" ;;
esac || error "Failed to extract $Format archive."
echo ""
cd /tmp/x11docker-install/$(ls -l | grep drwx | rev | cut -d' ' -f1 | rev) || error "Could not cd to /tmp/x11docker-update/$(ls -l | grep drwx | rev | cut -d' ' -f1 | rev)"
;;
esac
# Doing
case ${1:-} in
--install|--update|--update-master)
note "Installing x11docker and x11docker-gui in /usr/bin"
[ -e /usr/local/bin/x11docker ] && rm -v /usr/local/bin/x11docker
[ -e /usr/local/bin/x11docker-gui ] && rm -v /usr/local/bin/x11docker-gui
cp x11docker /usr/bin/ || error "Could not copy x11docker to /usr/bin"
chmod 755 /usr/bin/x11docker || error "Could not set executeable bit on x11docker"
cp x11docker-gui /usr/bin/ && chmod 755 /usr/bin/x11docker-gui || warning "x11docker-gui not found"
note "Installing icon for x11docker with xdg-icon-resource"
xdg-icon-resource install --context apps --novendor --mode system --size 64 "$(pwd)/x11docker.png" x11docker || warning "Could not install icon for x11docker.
Is 'xdg-icon-resource' (xdg-utils) installed on your system?"
xdg-icon-resource uninstall --size 72 x11docker ||: # deprecated icon size, may still be present.
note "Creating application entry for x11docker."
[ -e "/usr/bin/x11docker-gui" ] && {
echo "[Desktop Entry]
Version=1.0
Type=Application
Name=x11docker
Comment=Run GUI applications in docker images
Exec=x11docker-gui
Icon=x11docker
Categories=System
" > /usr/share/applications/x11docker.desktop
} || note "Did not create desktop entry for x11docker-gui"
command -v kaptain >/dev/null || note "Could not find 'kaptain' for x11docker-gui.
Consider to install 'kaptain' (version 0.73 or higher).
It's useful for x11docker-gui only, though. x11docker itself doesn't need it.
If your distributions does not provide kaptain, look at kaptain repository:
https://github.com/mviereck/kaptain
Fallback: x11docker-gui will try to use image x11docker/kaptain."
note "Storing README.md, CHANGELOG.md and LICENSE.txt in
/usr/share/doc/x11docker"
mkdir -p /usr/share/doc/x11docker && {
cp README.md /usr/share/doc/x11docker/
cp CHANGELOG.md /usr/share/doc/x11docker/
cp LICENSE.txt /usr/share/doc/x11docker/
} || note "Error while creating /usr/share/doc/x11docker"
Newversion="$(/usr/bin/x11docker --version)"
note "Installed x11docker version $Newversion"
;;
--remove)
note "Removing x11docker from your system."
cleanup
[ -x /usr/local/bin/x11docker ] && { # from older installations. /usr/bin is default now as /usr/local/bin can miss in $PATH for root
rm -v /usr/local/bin/x11docker
rm -v /usr/local/bin/x11docker-gui
}
[ -x /usr/bin/x11docker ] && {
rm -v /usr/bin/x11docker
rm -v /usr/bin/x11docker-gui
}
[ -e "/usr/share/applications/x11docker.desktop" ] && rm -v /usr/share/applications/x11docker.desktop
[ -e "/usr/share/doc/x11docker" ] && rm -R -v /usr/share/doc/x11docker
[ -e "/usr/share/icons/x11docker.png" ] && rm /usr/share/icons/x11docker.png
xdg-icon-resource uninstall --size 64 x11docker ||:
xdg-icon-resource uninstall --size 72 x11docker ||: # deprecated icon size, may still be present.
note "Will not remove files in your home folder.
There may be files left in \$HOME/.local/share/x11docker
The symbolic link \$HOME/x11docker may exist, too.
The cache folder \$HOME/.cache/x11docker should be removed already."
;;
esac
# Cleanup
case ${1:-} in
--update|--update-master)
note "Removing downloaded temporary files."
cd ~
rm -R /tmp/x11docker-install
;;
esac
# Changelog excerpt
case ${1:-} in
--update)
echo "$Oldversion" | grep -q beta && {
warning "You are switching from master branch to stable releases.
To get latest master beta version, use option --update-master instead"
Key1="\[${Newversion}\]"
Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
} || {
Key1="\[${Newversion}\]"
Key2="\[${Oldversion}\]"
[ "$Newversion" = "$Oldversion" ] && {
Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
note "Version $Newversion was already installed before this update.
If you want the latest beta version from master branch, use --update-master."
}
[ -z "$Oldversion" ] && Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
}
;;
--update-master)
echo "$Oldversion" | grep -q beta && {
Key1="\[Unreleased\]"
Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
} || {
Key1="\[Unreleased\]"
Key2="\[${Oldversion}\]"
[ -z "$Oldversion" ] && Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
}
;;
esac
case ${1:-} in
--update|--update-master)
note "Excerpt of x11docker changelog:
$(sed -n '/'$Key1'/,/'$Key2'/p' /usr/share/doc/x11docker/CHANGELOG.md | head -n-1)"
;;
esac
note "Ready."
}
#### features
check_windowmanager() { # option --wm: search a host window manager
# check --wm arguments, adjust mode
case "$Windowmanagermode" in
""|none)
Windowmanagermode="none"
return 0
;;
auto)
case $X11dockermode in
exe) Windowmanagermode="host" ;;
run) Windowmanagermode="container" ;;
esac
[ -n "$Windowmanagercommand" ] && {
command -v "$(cut -d' ' -f1 <<< "$Windowmanagercommand")" >/dev/null && {
Hostwindowmanager="$Windowmanagercommand"
Windowmanagermode="host"
} || {
note "Option --wm: Did not find command on host: $Windowmanagercommand
If a docker image with this name exists, x11docker will run it."
Windowmanagermode="container"
}
}
;;
host) ;;
container)
[ "$X11dockermode" = "exe" ] && {
note "Option --wm: With option --exe
x11docker does not support a containerized window manager yet.
Fallback: Setting --wm=host"
check_fallback
Windowmanagermode="host"
}
;;
esac
# Find a host window manager
[ "$Hostwindowmanager" ] || for Hostwindowmanager in $Wm_all WM_NOT_FOUND; do
command -v "$Hostwindowmanager" >/dev/null && break
done
#
case "$Windowmanagercommand" in
"")
case "$Windowmanagermode" in
container)
Windowmanagercommand="x11docker/openbox sh -c 'openbox --sm-disable --config-file /etc/x11docker/openbox-nomenu.rc'"
;;
host)
[ "$Hostwindowmanager" = "WM_NOT_FOUND" ] && {
Hostwindowmanager=""
note "Option --wm: No host window manager found.
Fallback: Setting --wm=none"
check_fallback
Windowmanagermode="none"
}
;;
esac
;;
esac
case "$Windowmanagermode" in ### FIXME warning does not appear if dockerrc runs host wm as a fallback
host)
[ "$Xtest" = "yes" ] && warning "Options --xtest --wm: Did not disable X extension XTEST
for X server $Xserver.
If your host window manager $Hostwindowmanager can start applications
on its own (for example with a context menu), container applications
can abuse this to run and remotely control host applications.
If you provide content of X server $Xserver over network to others,
they may take control over your host system!"
;;
esac
# command adjustment for some host window managers
case $(basename "$(cut -d' ' -f1 <<< "$Hostwindowmanager")") in
cinnamon|cinnamon-session) Hostwindowmanager="cinnamon --sm-disable";;
compiz) # if none, create minimal config to have useable window decoration and can move windows
[ -e "$Hostuserhome/.config/compiz-1/compizconfig/Default.ini" ] || {
unpriv "mkdir -p '$Hostuserhome/.config/compiz-1/compizconfig'"
mkfile "$Hostuserhome/.config/compiz-1/compizconfig/Default.ini"
echo '[core]
s0_active_plugins = core;composite;opengl;decor;resize;move;
' >> "$Hostuserhome/.config/compiz-1/compizconfig/Default.ini"
} ;;
enlightenment|e17|e16|e19|e20|e) Hostwindowmanager="enlightenment_start" ;;
matchbox) Hostwindowmanager="matchbox-window-manager" ;;
mate|mate-session) Hostwindowmanager="mate-session -f" ;;
mate-wm) Hostwindowmanager="marco --sm-disable" ;;
openbox)
Hostwindowmanager="openbox --sm-disable"
[ -e "/etc/xdg/openbox/rc.xml" ] && {
cp /etc/xdg/openbox/rc.xml $Sharefolder/openbox-nomenu.rc
sed -i /ShowMenu/d $Sharefolder/openbox-nomenu.rc
sed -i s/NLIMC/NLMC/ $Sharefolder/openbox-nomenu.rc
Hostwindowmanager="$Hostwindowmanager --config-file $Sharefolder/openbox-nomenu.rc"
}
;;
esac
verbose "Detected host window manager: ${Hostwindowmanager:-"(none)"}"
return 0
}
clean_xhost() { # option --clean-xhost: disable xhost policies on host X
[ -z "$Hostdisplay" ] && note "Option --clean-xhost: No host X display found." && return 1
[ -z "$Hostxauthority" ] && warning "Option --clean-xhost: You host X server does not provide
an authentication cookie in \$XAUTHORITY.
Host applications started after xhost cleanup might fail to start."
echo "Option --clean-xhost:" 2>&1 >> $Xinitlogfile
DISPLAY="$Hostdisplay" XAUTHORITY="$Hostxauthority" disable_xhost 2>&1 >> $Xinitlogfile
}
setup_clipboard() { # option --clipboard: create shareclipboard script
# Clipboard sharing works different depending on the new X server
# - xpra and nxagent have their own clipboard management.
# - Only xpra supports image clips.
# - --hostdisplay accesses the clipboard from host X directly
# - Other X servers: A script is created to synchronize clipboard between X servers.
# - No clipboard support for Wayland yet.
# The script uses xclip or xsel. It is executed in xinitrc.
local Clipsend= Clipreceive=
case $Xserver in
--tty|--weston|--hostwayland|--kwin)
warning "Option --clipboard is not supported for $Xserver.
Fallback: Disabling option --clipboard."
check_fallback
Shareclipboard="no"
return 1
;;
--nxagent|--xpra|--xpra-xwayland|--xwin) ;; # have their own clipboard management, look at create_xcommand().
--xephyr|--xorg|--xdummy|--xdummy-xwayland|--xvfb|--xwayland|--weston-xwayland)
# check for either xclip or xsel
command -v xsel >/dev/null && {
Clipsend="xsel --clipboard --input"
Clipreceive="xsel --clipboard --output"
}
command -v xclip >/dev/null && {
Clipsend="xclip -selection clipboard -in"
Clipreceive="xclip -selection clipboard -out"
}
[ -z "$Clipsend" ] && {
note "Option --clipboard: Need either xclip or xsel
for clipboard sharing with X server $Xserver.
Fallback: Disabling option --clipboard."
check_fallback
Shareclipboard="no"
}
[ -z "$Hostdisplay" ] && {
note "Option --clipboard: No host display DISPLAY found
to share clipboard. Fallback: Disabling option --clipboard"
check_fallback
Shareclipboard="no"
}
echo "#! /usr/bin/env bash
# share clipboard between X servers $Hostdisplay and $Newdisplay
$(declare -f mysleep)
$(declare -f rocknroll)
Timetosaygoodbyefile='$Timetosaygoodbyefile'
while rocknroll ; do
# read content of clipboard of first X server $Hostdisplay
X1clip=\"\$(env DISPLAY=$Hostdisplay XAUTHORITY=$Hostxauthority $Clipreceive)\"
# check if clipboard of first X server has changed; if yes, send new content to second X server
[ \"\$Shareclip\" != \"\$X1clip\" ] && {
Shareclip=\"\$X1clip\"
env DISPLAY=$Newdisplay XAUTHORITY=$Xclientcookie $Clipsend <<< \"\$Shareclip\"
# echo \"\$Shareclip\" | env DISPLAY=$Newdisplay XAUTHORITY=$Xclientcookie $Clipsend
}
Shareclip=\"\${Shareclip:-' '}\" # avoid empty string error
mysleep 0.3 # sleep a bit to avoid high cpu usage
# read content of clipboard of second X server $Newdisplay
X2clip=\"\$(env DISPLAY=$Newdisplay XAUTHORITY=$Xclientcookie $Clipreceive)\"
# check if clipboard of second X server has changed; if yes, send new content to first X server
[ \"\$Shareclip\" != \"\$X2clip\" ] && {
Shareclip=\"\$X2clip\"
env DISPLAY=$Hostdisplay XAUTHORITY=$Hostxauthority $Clipsend <<< \"\$Shareclip\"
# echo \"\$Shareclip\" | env DISPLAY=$Hostdisplay XAUTHORITY=$Hostxauthority $Clipsend
}
Shareclip=\"\${Shareclip:-' '}\" # avoid empty string error
mysleep 0.3 # sleep a bit to avoid high cpu usage
done
" >> $Clipboardrc
;;
esac
return 0
}
setup_gpu() { # option --gpu: share /dev/dri and check nvidia driver
# Easiest case: share /dev/dri.
# Works for open source MESA drivers on host and in image.
# Debian packages for MESA drivers in image: libgl1-mesa-dri, libglx-mesa0
#
# Closed source NVIDIA drivers does not integrate well within linux.
# Instead, free nouveau driver is a better choice, or no NVIDIA hardware at all.
# Posibilities:
# - Install NVIDIA driver in image. It must be the very same version as on your host.
# The image is not portable anymore.
# - x11docker can install NVIDIA driver on the fly in running container. See notes below.
#
# g $Nvidiainstallerfile nvidia driver file to install in container in containerrootrc
# g $Nividaversion nvidia driver version on host
local Gpudevice
Containerusergroups="$Containerusergroups video render"
# check device files
while read -r Gpudevice ; do
store_runoption volume "$Gpudevice"
done < <(find /dev/dri /dev/nvidia* /dev/vga_arbiter /dev/nvhost* /dev/nvmap -maxdepth 0 2>/dev/null ||:)
[ -z "$Nvidiaversion" ] && return 0
# check for closed source nvidia driver on host, provide automated installation, warn about disadvantages
debugnote "NVIDIA: Detected driver version $Nvidiaversion on host."
[ "$Runtime" = "nvidia" ] && {
debugnote "NVIDIA: Option --runtime=nvidia: Skipping driver installation."
Nvidiainstallerfile=""
return 0
}
Nvidiainstallerfile="$(find /usr/local/share/x11docker/NVIDIA*$Nvidiaversion*.run $Hostuserhome/.local/share/x11docker/NVIDIA*$Nvidiaversion*.run 2>/dev/null | tail -n1 )"
Nvidiainstallerfile="$(myrealpath "$Nvidiainstallerfile" 2>/dev/null)"
[ -e "$Nvidiainstallerfile" ] && {
debugnote "NVIDIA: Found proprietary closed source NVIDIA driver installer
$Nvidiainstallerfile"
return 0
}
Nvidiainstallerfile=""
note "Option --gpu: You are using the closed source NVIDIA driver.
GPU acceleration will only work if you have installed the very same driver
version in image. That makes images less portable.
It is recommended to use free open source nouveau driver on host instead.
Ask NVIDIA corporation to at least publish their closed source API,
or even better to actively support open source driver nouveau."
note "Option --gpu: x11docker can try to automatically install NVIDIA driver
version $Nvidiaversion in container on every container startup.
Drawbacks: Container startup is a bit slower and its security will be reduced.
You can look here for a driver installer:
https://www.nvidia.com/Download/index.aspx
https://http.download.nvidia.com/
A direct download URL is probably:
https://http.download.nvidia.com/XFree86/Linux-x86_64/$Nvidiaversion/NVIDIA-Linux-x86_64-$Nvidiaversion.run
If you got a driver, store it at one of the following locations:
$Hostuserhome/.local/share/x11docker/
/usr/local/share/x11docker/
Be aware that the version number must match exactly the version on host.
The file name must begin with 'NVIDIA', contain the version number $Nvidiaversion
and end with suffix '.run'."
return 0
}
setup_hostdbus() { # option --hostdbus: connect to host DBus session daemon.
warning "--hostdbus: Connecting container to host DBus degrades
container isolation. Container applications might send malicious requests."
Dbusrunsession=no
[ "$DBUS_SESSION_BUS_ADDRESS" ] || {
# no running DBus session?
command -v dbus-launch >/dev/null && {
export $(dbus-launch)
note "Option --hostdbus: DBUS_SESSION_BUS_ADDRESS is empty.
Creating abstract DBus socket with dbus-launch."
} || note "Option --hostdbus: Is DBus running on host?
Did not find an active session and did not find dbus-launch.
DBUS_SESSION_BUS_ADDRESS is empty.
$Wikipackages"
}
grep -q "unix:path" <<< "$DBUS_SESSION_BUS_ADDRESS" && {
# DBus socket file
store_runoption env "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"
store_runoption volume "/$(cut -d/ -f2- <<<"$DBUS_SESSION_BUS_ADDRESS"):ro"
}
grep -q "unix:abstract" <<< "$DBUS_SESSION_BUS_ADDRESS" && {
# DBus abstract socket (dbus-launch)
store_runoption env "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"
[ "${DBUS_SESSION_BUS_PID:-}" ] && store_runoption env "${DBUS_SESSION_BUS_PID:-}"
[ "${DBUS_SESSION_BUS_WINDOWID:-}" ] && store_runoption env "${DBUS_SESSION_BUS_WINDOWID:-}"
Network="host"
warning "Option --hostdbus: Did not find a DBus session socket file
but an abstract unix socket. To allow access for container,
x11docker sets option '--network=host'.
This degrades container isolation. Container shares host network stack."
}
return 0
}
setup_printer() { # option --printer: connect to cups printer server
# Default CUPS setups create a unix socket /run/cups/cups.sock as given from 'lpstat -H'.
# Sharing this socket and pointing environment variable CUPS_SERVER to it serves most cases.
# Possible CUPS network setups need to allow access from container, see note below.
local Cupsserver=
command -v lpstat >/dev/null || {
warning "Option --printer: command lpstat not found.
Is cups printer server installed on your system?
Error: Cannot share access to printer."
Sharecupsmode=""
return 1
}
case $Sharecupsmode in
tcp) Cupsserver="$Hostip:631" ;;
socket) Cupsserver="$(lpstat -H)" ;;
esac
grep -q ":" <<<$Cupsserver && {
[ "$(cut -d: -f1 <<<$Cupsserver)" = "localhost" ] && Cupsserver="$Hostip:$(cut -d: -f2 <<<$Cupsserver)"
[ "$(cut -d: -f1 <<<$Cupsserver)" = "127.0.0.1" ] && Cupsserver="$Hostip:$(cut -d: -f2 <<<$Cupsserver)"
note "Option --printer: Network setup for CUPS detected.
Server address: $Cupsserver
You may need to allow container access in /etc/cups/cupsd.conf, e.g.:
Port 631
<Location />
# Allow remote access...
Order allow,deny
Allow 172.17.0.*
Allow 127.0.0.1
</Location>"
}
[ "$Cupsserver" ] && store_runoption env "CUPS_SERVER=$Cupsserver"
[ -e "$Cupsserver" ] && {
[ "$(dirname $Cupsserver)" = "/run/cups" ] && store_runoption volume "/run/cups" || store_runoption volume "$Cupsserver"
}
return 0
}
setup_sound_alsa() { # option --alsa: share sound devices
# Sound with ALSA is directly supported by the kernel and only needs to share devices in /dev/snd.
# libasound2 in image is rcommended.
# The desired sound card can be specified with environment variable ALSA_CARD. See card name in 'aplay -l'.
# Further documentation at https://github.com/mviereck/x11docker/wiki/Container-sound:-ALSA-or-Pulseaudio
warning "ALSA sound with option --alsa degrades container isolation.
Shares device files in /dev/snd, container gains access to sound hardware.
Container applications can catch audio output and microphone input."
[ "$Alsacard" ] && store_runoption env "ALSA_CARD=$Alsacard"
pgrep pulseaudio >/dev/null && note "It seems that pulseaudio is running on your host.
Pulseaudio can interfere with ALSA sound (option --alsa).
Host sound may not work while container is playing sound and vice versa.
Alternative: with pulseaudio on host and in image, use option --pulseaudio."
[ -d /dev/snd ] && store_runoption volume "/dev/snd" || {
warning "Option --alsa: /dev/snd not found.
Sound support not possible."
Sharealsa="no"
return 1
}
[ -s "$Pulseaudioconf" ] || echo "# Connect to host pulseaudio server using mounted UNIX socket
default-server = none
# Prevent a server running in container
autospawn = no
daemon-binary = /bin/true
# Prevent use of shared memory
enable-shm = false
" >> $Pulseaudioconf
[ "$Sharealsa" = "yes" ] && Containerusergroups="$Containerusergroups audio"
return 0
}
setup_sound_pulseaudio() { # option --pulseaudio: set up pulseaudio connection
# Allowing container access to Pulseaudio on host can be done with a shared socket or over TCP.
# Sharing host user socket in XDG_RUNTIME_DIR fails since Pulseaudio v12.0.
# Instead, a new socket is created with pactl.
# TCP module is created after container startup to authenticate it with container IP.
# Detailed documentation at: https://github.com/mviereck/x11docker/wiki/Container-sound:-ALSA-or-Pulseaudio
#
# g $Pulseaudiomode =tcp or =socket: Connect over tcp or with shared socket
# g $Pulseaudioport TCP port
local Lowerport= Upperport=
local Pulseaudiopath
warning "Option --pulseaudio allows container applications
to catch your audio output and microphone input."
[ -z "$Pulseaudiomode" ] && Pulseaudiomode="socket"
[ "$Pulseaudiomode" = "auto" ] && {
Pulseaudiomode="socket"
[ "$Containeruser" = "$Hostuser" ] || Pulseaudiomode="tcp"
[ "$Runtime" = "kata-runtime" ] && Pulseaudiomode="tcp"
[ "$Runsinsnap" = "yes" ] && Pulseaudiomode="tcp"
LC_ALL=C pactl info | grep -q "User Name: pulse" && Pulseaudiomode="tcp"
}
case $Pulseaudiomode in
socket)
# create pulseaudio socket
Pulseaudiomoduleid="$(unpriv "pactl load-module module-native-protocol-unix socket=$Pulseaudiosocket 2>&1")"
[ "$Pulseaudiomoduleid" ] && {
storeinfo "pulseaudiomoduleid=$Pulseaudiomoduleid"
store_runoption env "PULSE_SERVER=unix:$(convertpath share $Pulseaudiosocket)"
store_runoption env "PULSE_COOKIE=$(convertpath share $Pulseaudiocookie)"
} || {
note "Option --pulseaudio: command pactl failed.
Is pulseaudio running at all on your host?
Fallback: Enabling option --alsa"
check_fallback
Pulseaudiomode=""
Sharealsa="yes"
}
echo "# Connect to host pulseaudio server using mounted UNIX socket
default-server = unix:$(convertpath share $Pulseaudiosocket)
# Prevent a server running in container
autospawn = no
daemon-binary = /bin/true
# Prevent use of shared memory
enable-shm = false
" >> $Pulseaudioconf
verbose "Generated pulseaudio client.conf:
$(nl -ba <$Pulseaudioconf)"
;;
tcp)
read Lowerport Upperport < /proc/sys/net/ipv4/ip_local_port_range 2>/dev/null
[ "$Lowerport" ] || Lowerport=33000
[ "$Upperport" ] || Upperport=60000
while : ; do
Pulseaudioport="$(shuf -i $Lowerport-$Upperport -n1)"
ss -lpn | grep -q ":$Pulseaudioport " || break
done
[ -e "$Hostuserhome/.config/pulse/cookie" ] && cp "$Hostuserhome/.config/pulse/cookie" "$Pulseaudiocookie" || note "Option --pulseaudio: Did not find cookie
$Hostuserhome/.config/pulse/cookie"
store_runoption env "PULSE_SERVER=tcp:$Hostip:$Pulseaudioport"
;;
esac
return 0
}
setup_webcam() { # option --webcam: share webcam devices
# Webcam devices appear as /dev/video* files.
# Unprivileged users need to be in group video.
# (This works only if webcam is plugged in before container starts.
# Hotplug support would have to be different.)
local Webcamdevice
while read -r Webcamdevice ; do
store_runoption volume "$Webcamdevice"
done < <(find /dev/video* -maxdepth 0 2>/dev/null || note "Option --webcam: No webcam devices /dev/video* found.
Webcam in container will fail.")
Containerusergroups="$Containerusergroups video"
# at least cheese and gnome-ring need some device information from udev.
store_runoption volume "/run/udev/data:ro"
}
#### X server setup
check_newxenv() { # find free display, create $Newxenv
local Line
# find free display number
[ "$Newdisplaynumber" ] || {
Newdisplaynumber="100"
while :; do
case $Xserver in
--xwin|--runx) Newdisplaynumber="$((RANDOM / 10 + 200))" ;;
*) Newdisplaynumber="$((Newdisplaynumber + 1))" ;;
esac
grep -q -x "$Newdisplaynumber" < "$Numbersinusefile" || [ -n "$(find "/tmp/.X11-unix/X$Newdisplaynumber" "/tmp/.X$Newdisplaynumber-lock" "$XDG_RUNTIME_DIR/wayland-$Newdisplaynumber" 2>/dev/null)" ] || break
done
}
echo "$Newdisplaynumber" >> "$Numbersinusefile"
# X over IP/TCP
[ "$Xoverip" ] || case $Xserver in
--xwin|--runx) Xoverip="yes" ;;
esac
# set $Newdisplay (DISPLAY of container) and $Newxsocket
case $Xserver in
--hostdisplay)
case $Xoverip in
yes)
[ "$(cut -c1 <<< "$Hostdisplay")" = ":" ] && Newdisplay="${Hostip}${Hostdisplay}" || Newdisplay="$Hostdisplay" ;;
no|"")
Newdisplay="$Hostdisplay"
Newdisplaynumber="$(echo $Newdisplay | cut -d: -f2 | cut -d. -f1)"
Newxsocket="$Hostxsocket"
;;
esac
;;
--weston|--kwin|--hostwayland|--tty)
Newdisplay=""
Newxsocket=""
Xclientcookie=""
Xservercookie=""
;;
*)
case $Xoverip in
yes) Newdisplay="$Hostip:$Newdisplaynumber" ;;
no|"")
Newdisplay=":$Newdisplaynumber"
Newxsocket="/tmp/.X11-unix/X$Newdisplaynumber"
Newxlock="/tmp/.X$Newdisplaynumber-lock"
[ -n "$(find $Newxsocket $Newxlock 2>/dev/null)" ] && error "Display $Newdisplay is already in use."
;;
esac
;;
esac
# set $Newwaylandsocket
case $Xserver in
--weston|--weston-xwayland|--kwin|--kwin-xwayland|--xpra-xwayland|--xdummy-xwayland) Newwaylandsocket="wayland-$Newdisplaynumber" ;;
--hostwayland|--xwayland) Newwaylandsocket="$Hostwaylandsocket" ;;
esac
# create $Newxenv: collection of environment variables to access new X from host (e.g. in xinitrc)
[ "$Newdisplay" ] && storeinfo "DISPLAY=$Newdisplay" && Newxenv="$Newxenv DISPLAY=$Newdisplay"
[ -e "$Xclientcookie" ] && storeinfo "XAUTHORITY=$Xclientcookie" && Newxenv="$Newxenv XAUTHORITY=$Xclientcookie"
[ "$Newxsocket" ] && storeinfo "XSOCKET=$Newxsocket" && Newxenv="$Newxenv XSOCKET=$Newxsocket"
[ "$Newwaylandsocket" ] && storeinfo "WAYLAND_DISPLAY=$Newwaylandsocket" && Newxenv="$Newxenv WAYLAND_DISPLAY=$Newwaylandsocket"
[ "$Setupwayland" = "yes" ] && for Line in $Waylandtoolkitenv ; do Newxenv="$Newxenv $Line" ; done
storeinfo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" && Newxenv="$Newxenv XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
storeinfo "Xenv=$Newxenv"
# X / Wayland environment variables for container
case $Xserver in
--xpra|--xephyr|--xpra-xwayland|--weston-xwayland|--hostdisplay|--xorg|--xdummy|--xvfb|--xdummy-xwayland|--xwayland|--kwin-xwayland|--nxagent|--xwin|--runx)
store_runoption env "DISPLAY=$Newdisplay"
store_runoption env "XAUTHORITY=$(convertpath share $Xclientcookie)"
;;
--weston|--kwin|--hostwayland|--tty)
store_runoption env "WAYLAND_DISPLAY=$Newwaylandsocket"
;;
esac
return 0
}
check_screensize() { # check physical and virtual screen size (also option --size)
local Line=
[ -z "$Hostdisplay" ] && [ -n "$Hostwaylandsocket" ] && verbose "check_screensize(): Skipping check on pure Wayland environment"
# check whole display size, can include multiple monitors
[ -n "$Hostdisplay" ] && {
command -v xrandr >/dev/null && {
Line="$(xrandr 2>/dev/null | grep current | head -n1 | cut -d, -f2)"
Maxxaxis="$(echo "$Line" | cut -d' ' -f3)"
Maxyaxis="$(echo "$Line" | cut -d' ' -f5)"
}
[ -z "$Maxxaxis" ] && command -v xdpyinfo >/dev/null && {
Line="$(xdpyinfo | grep dimensions)"
Maxxaxis="$(echo "$Line" | cut -dx -f1 | rev | cut -d ' ' -f1 | rev)"
Maxyaxis="$(echo "$Line" | cut -dx -f2 | cut -d ' ' -f1)"
}
[ -z "$Maxxaxis" ] && command -v xwininfo >/dev/null && {
Line="$(xwininfo -root -stats)"
Maxxaxis="$(echo "$Line" | grep Width | rev | cut -d' ' -f1 | rev)"
Maxyaxis="$(echo "$Line" | grep Height | rev | cut -d' ' -f1 | rev)"
}
[ -z "$Maxxaxis" ] && note "check_screensize(): Could not determine your screen size.
Please improve this by installing one of xrandr, xdpyinfo or xwininfo.
Or use option --size=XxY.
$Wikipackages"
}
case $Xserver in
--xvfb|--xdummy)
[ "$Screensize" ] && {
Maxxaxis="${Screensize%x*}"
Maxyaxis="${Screensize#*x}"
} || {
Maxxaxis=4720
Maxyaxis=3840
note "Option $Xserver: Specifying quite big virtual screen size
for $Xserver: ${Maxxaxis}x${Maxyaxis}
This costs some memory, but will fit most possible remote screens.
To save memory, specify needed screen size only with e.g. --size=1980x1200
Check output of 'xrandr | grep current' on your target display."
}
;;
esac
[ -n "$Maxxaxis" ] && {
Xaxis="$Maxxaxis"
Yaxis="$Maxyaxis"
}
[ "$Fullscreen" = "yes" ] && [ "$Runsonconsole" = "no" ] && [ -n "$Maxxaxis" ] && Screensize="${Maxxaxis}x${Maxyaxis}"
# size for windowed desktops, roughly maximized relative to primary monitor
case $Xserver in
--xpra|--xpra-xwayland) [ "$Desktopmode" = "yes" ] && Xserver="${Xserver}-desktop" ;;
esac
case $Xserver in
--xephyr|--weston-xwayland|--weston|--kwin|--kwin-xwayland|--nxagent|--xpra-desktop|--xpra-xwayland-desktop)
[ "$Runsonconsole" = "yes" ] && {
: # nothing to do on tty. ### FIXME maybe should check --size=$Screensize
} || {
command -v xrandr > /dev/null && xrandr 2>/dev/null | grep -q ' connected' && { # reduce size to primary monitor for windowed desktop
Xaxis="$(xrandr 2>/dev/null | grep ' connected' | head -n1 | cut -dx -f1 | rev | cut -d' ' -f1 | rev)"
Yaxis="$(xrandr 2>/dev/null | grep ' connected' | head -n1 | cut -dx -f2 | cut -d' ' -f1 | cut -d+ -f1)"
Xaxis="$((Xaxis-96))"
Yaxis="$((Yaxis-96))"
Xaxis="$(( $(( $Xaxis / 8 )) * 8 ))" # avoid grey edge in Xwayland, needs full byte x width
} || {
note "Could not determine size of your primary display to
create a roughly maximized window for $Xserver.
Please install xrandr or use option --size=XxY.
Fallback: setting virtual screen size 800x600
$Wikipackages"
Xaxis="800"
Yaxis="600"
}
}
;;
esac
Xserver="${Xserver%-desktop}"
[ -z "$Xaxis" ] && { ### FIXME: arbitrary resolution. At least, --xorg checks again with xrandr in xinitrc
Xaxis="4720"
Yaxis="3840"
}
# regard scaling (option --scale)
[ "$Scaling" ] && {
Xaxis="$(awk -v a=$Xaxis -v b=$Scaling 'BEGIN {print (a / b)}')"
Xaxis="${Xaxis%.*}"
Yaxis="$(awk -v a=$Yaxis -v b=$Scaling 'BEGIN {print (a / b)}')"
Yaxis="${Yaxis%.*}"
}
[ -n "$Screensize" ] && { # regard --size, overwriting Xaxis/Yaxis from above
Xaxis="${Screensize%x*}"
Yaxis="${Screensize#*x}"
}
case $Xserver in
--xorg) ;; # Xorg autodetects screen size, preset only with option --size
*) [ "$Runsonconsole" = "no" ] && Screensize="${Xaxis}x${Yaxis}" ;;
esac
[ -z "$Maxxaxis" ] && {
Maxxaxis="$Xaxis"
Maxyaxis="$Yaxis"
}
[ "$Xaxis" -gt "$Maxxaxis" ] && Maxxaxis="$Xaxis"
[ "$Yaxis" -gt "$Maxyaxis" ] && Maxyaxis="$Yaxis"
command -v cvt >/dev/null && Modeline="$(cvt $Xaxis $Yaxis | tail -n1 | cut -d' ' -f2-)"
Modeline="$(echo $Modeline | cut -d_ -f1)\" $(echo $Modeline | cut -d_ -f2- | cut -d' ' -f2-)"
verbose "Virtual screen size: $Screensize"
verbose "Physical screen size:
$(xrandr 2>/dev/null | grep Screen ||:)"
# create set of Modelines if needed
{ [ "$Xserver" = "--xpra" ] && [ "$Xpravfb" = "Xvfb" ] && [ "$Desktopmode" = "yes" ] ; } || [ "$Xserver" = "--xvfb" ] && {
Modelinefile="$(create_modelinefile ${Maxxaxis}x${Maxyaxis})"
}
return 0
}
check_vt() { # option --xorg: find free vt / tty
local Line= Ttyinuse=
# if started from console, use current tty
tty -s && [ "$Runsonconsole" = "yes" ] && {
Newxvt="$(tty | rev | cut -d/ -f1 | rev)"
Newxvt="${Newxvt#tty}"
}
# check ttys currently in use
[ "$Newxvt" ] || {
for Line in $(find /sys/class/vc/vcsa*); do
Ttyinuse="$Ttyinuse ${Line#/sys/class/vc/vcsa} "
done
debugnote "check_vt(): TTYs currently known to kernel: $Ttyinuse"
}
[ "$Newxvt" ] && grep -q " $Newxvt " <<< "$Ttyinuse" && warning "TTY $Newxvt seems to be already in use."
# try to find free tty within range of 8..12
[ "$Newxvt" ] || {
for ((Newxvt=8 ; Newxvt<=12 ; Newxvt++)) ; do
grep -q " $Newxvt " <<< "$Ttyinuse" || break
done
}
# try to find free tty within range of 1..7
[ "$Newxvt" ] || {
for ((Newxvt=1 ; Newxvt<=7 ; Newxvt++)) ; do
grep -q " $Newxvt " <<< "$Ttyinuse" || break
done
}
# try to find free tty with fgconsole. Fails in some cases within X.
[ "$Newxvt" ] || Newxvt="$(fgconsole --next-available 2>/dev/null)"
[ "$Newxvt" ] || Newxvt="$(fgconsole --next-available 2>/dev/null </dev/tty${XDG_VTNR:-})"
# try to find free tty within range of 13..63
[ "$Newxvt" ] || {
for ((Newxvt=13 ; Newxvt<=63 ; Newxvt++)) ; do
grep -q " $Newxvt " <<< "$Ttyinuse" || break
done
}
[ "$Newxvt" ] || error "Could not identify a free tty for --xorg."
[ "${XDG_VTNR:-}" ] && [ "$Hostdisplay$Hostwaylandsocket" ] && note "Current X server $Hostdisplay runs on tty ${XDG_VTNR:-}.
Access it with [CTRL][ALT][F${XDG_VTNR:-}]."
[ "${Newxvt:-999}" -gt "12" ] && {
fgconsole --next-available 1>/dev/null 2>/dev/null || note "Could not check for a free tty below or equal to 12.
Would need to use command fgconsole for a better check.
Possibilities:
1.) Run x11docker as root.
2.) Add user to group tty (not recommended, may be insecure).
3.) Use display manager gdm3.
4.) Run x11docker directly from console."
note "To access X on tty$Newxvt, use command 'chvt $Newxvt'"
} || {
note "New Xorg server $Newdisplay will run on tty $Newxvt.
Access it with [CTRL][ALT][F$Newxvt]."
}
warning "On debian 9, switching often between multiple X servers can
cause a crash of one X server. This bug may be debian specific and is probably
some sort of race condition. If you know more about this or it occurs on
other systems, too, please report at https://github.com/mviereck/x11docker.
You can avoid this issue with switching to a black tty before switching to X."
return 0
}
check_xdepends() { # check dependencies on host for X server option $1
# Return 1 if something is missing
local Return= Message=
[ "$Lastcheckedxserver" = "${1:-}" ] && debugnote "Dependencies of ${1:-} already checked: $Lastcheckedxserverresult " && return $Lastcheckedxserverresult
case $Autochooseserver in
yes) Message="debugnote" ;;
no) Message="note" ;;
esac
case ${1:-} in
--xephyr|--xpra|--nxagent|--xorg|--xvfb|--xdummy|--xwayland|--weston-xwayland|--kwin-xwayland|--xdummy-xwayland)
command -v xinit >/dev/null || {
$Message "${1:-}: xinit not found."
Return=1
}
;;
esac
case ${1:-} in
--xpra-xwayland|--weston-xwayland|--xwayland|--weston|--kwin|--kwin-xwayland|--xdummy-xwayland|--hostwayland)
[ "$Nvidiaversion" ] && {
$Message "${1:-}: Closed source NVIDIA driver does not support Wayland."
Return=1
}
[ "$Runtime" = "kata-runtime" ] && {
$Message "${1:-} not supported with --runtime=kata-runtime"
Return=1
}
case $Mobyvm in
yes)
$Message "${1:-} not supported with MobyVM / docker-for-win"
Return=1
;;
esac
;;
esac
case ${1:-} in
--xpra|--xpra-xwayland)
command -v "xpra" >/dev/null || {
$Message "${1:-}: xpra not found.
$Wikipackages"
Return=1
} ;;
--xephyr)
command -v "Xephyr" >/dev/null || command -v "Xnest" >/dev/null || {
$Message "${1:-}: Neither Xephyr nor Xnest found.
$Wikipackages"
Return=1
} ;;
--nxagent)
command -v "nxagent" >/dev/null || {
$Message "${1:-}: nxagent not found.
$Wikipackages"
Return=1
} ;;
--xvfb)
command -v "Xvfb" >/dev/null || {
$Message "${1:-}: Xvfb not found.
$Wikipackages"
Return=1
} ;;
--xorg|--xdummy)
command -v "Xorg" >/dev/null || {
$Message "${1:-}: Xorg not found.
$Wikipackages"
Return=1
} ;;
--xwin)
case "$Winsubsystem" in
CYGWIN)
command -v XWin >/dev/null || {
$Message "${1:-}: XWin not found.
Need packages 'xinit', 'xauth' and 'xwininfo' in Cygwin (X11 section)."
Return=1
}
;;
WSL1|WSL2)
$Message "${1:-}: XWin is available in Cygwin on MS Windows only.
Use runx to provide XWin in WSL: https://github.com/mviereck/runx"
Return=1
;;
MSYS2)
$Message "${1:-}: XWin is available in Cygwin on MS Windows only.
With runx XWin is available in WSL, too.
In MSYS2 you can only use runx with VcXsrv to provide an X server:
https://github.com/mviereck/runx"
Return=1
;;
"")
$Message "${1:-}: XWin is available in Cygwin on MS Windows only."
Return=1
;;
esac
command -v xwininfo >/dev/null || {
$Message "${1:-}: xwininfo not found.
Need 'xwininfo' package from Cygwin/X (X11 section)."
Return=1
}
[ "$Hostip" ] || {
$Message "${1:-}: Failed to get host IP address."
Return=1
}
;;
--runx)
[ "$Winsubsystem" ] || {
$Message "${1:-}: runx is available on MS Windows only."
Return=1
}
command -v runx >/dev/null || {
$Message "${1:-}: runx not found.
Need runx from https://github.com/mviereck/runx"
Return=1
}
;;
esac
case ${1:-} in
--weston|--xpra-xwayland|--weston-xwayland|--xdummy-xwayland)
command -v "weston" >/dev/null || {
$Message "${1:-}: weston not found.
$Wikipackages"
Return=1
} ;;
--kwin|--kwin-xwayland)
command -v "kwin_wayland" >/dev/null || {
$Message "${1:-}: kwin_wayland not found.
$Wikipackages"
Return=1
} ;;
esac
case ${1:-} in
--xpra-xwayland|--weston-xwayland|--kwin-xwayland|--xwayland|--xdummy-xwayland)
command -v "Xwayland" >/dev/null || {
$Message "${1:-}: Xwayland not found.
$Wikipackages"
Return=1
} ;;
esac
case ${1:-} in
--xpra-xwayland|--xdummy-xwayland)
command -v "xdotool" >/dev/null || {
$Message "${1:-}: xdotool not found.
$Wikipackages"
Return=1
} ;;
esac
case ${1:-} in
--xpra|--xpra-xwayland)
[ "$Return" = "1" ] || {
# check xpra version
[ "$Xpraversion" ] || {
Xpraversion="$(xpra --version 2>/dev/null | cut -d' ' -f2)"
Xprarelease="$(echo $Xpraversion | cut -s -d- -f2)"
verbose "Xpra version: ${Xpraversion:-XPRA_NOT_FOUND}"
[ "$Xprahelp" ] || Xprahelp="$(xpra --help 2>/dev/null)"
}
! verlte "$Xprarelease" "r18663" && verlte $Xprarelease "r19519" && {
[ "$Sharehostipc" = "no" ] && {
$Message "Your xpra version has a MIT-SHM bug that would force
x11docker to share host IPC namespace. That would reduce container isolation.
Current installed version: xpra $Xpraversion
Please update to at least xpra v2.3.1-19519 or xpra v2.4-r19520,
or downgrade to xpra v2.2.5 or lower, or use another X server option.
If you insist on using current xpra, set insecure option --hostipc.
Fallback: will search for another available X server setup."
Return=1
}
}
}
;;
esac
case ${1:-} in
--xpra)
[ -z "$Hostdisplay" ] && [ -n "$Hostwaylandsocket" ] && {
verlt $Xprarelease r23305 && {
$Message "Option ${1:-}: xpra on Wayland needs at least xpra v3.0-r23305
with python3 backend."
Return=1
} || {
$Message "Option ${1:-}: Support in pure Wayland is experimental
and needs latest xpra v3.x versions with python3 backend.
If issues occure, use --weston-xwayland, --kwin-xwayland or --hostwayland
or use option ${1:-} in an X environment."
}
} || {
[ "$Hostdisplay" ] || {
$Message "${1:-} needs a running X server. DISPLAY is empty. Wayland support is experimental option."
Return=1
}
}
;;
--hostdisplay|--xpra-xwayland|--xdummy-xwayland|--xephyr|--nxagent)
[ "$Hostdisplay" ] || {
$Message "${1:-} needs a running X server. DISPLAY is empty."
Return=1
}
;;
--hostwayland|--xwayland)
[ "$Hostwaylandsocket" ] || {
$Message "${1:-} needs a running Wayland compositor. WAYLAND_DISPLAY is empty."
Return=1
}
;;
esac
[ "$Winsubsystem" ] && {
case ${1:-} in
--tty|--xwin|--runx) ;;
--xpra|--xpra-xwayland)
$Message "${1:-} is not supported on MS Windows."
Return=1
;;
*)
[ -z "$Hostdisplay" ] && {
case $Winsubsystem in
Cygwin) $Message "${1:-} needs a running X server. DISPLAY is empty.
Please install packages in Cygwin: xinit xauth xwininfo
or use runx to provide an X server on MS Windows:
https://github.com/mviereck/runx" ;;
MSYS2|WSL1|WSL2) $Message "${1:-} needs a running X server. DISPLAY is empty.
Please use runx to provide an X server on MS Windows:
https://github.com/mviereck/runx" ;;
esac
Return=1
}
;;
esac
}
[ "$Xoverip" = "yes" ] && {
case ${1:-} in
--xephyr|--xorg|--nxagent|--xpra|--xvfb|--xdummy|--xwin|--runx|--tty) ;;
--hostdisplay)
[ -n "$(cut -d: -f1 <<< "$Hostdisplay")" ] || {
$Message "${1:-} does not support X over TCP
except if started remotely with 'ssh -X'."
Return=1
}
;;
--xwayland|--weston-xwayland|--kwin|--kwin-xwayland|--xpra-xwayland|--xdummy-xwayland|--hostwayland)
$Message "${1:-} does not support X over TCP."
Return=1
;;
esac
}
[ "$Sharegpu" = "yes" ] && {
case ${1:-} in
--hostdisplay|--xorg|--xpra-xwayland|--weston-xwayland|--kwin-xwayland|--xdummy-xwayland|--xwayland|--xwin|--runx) ;;
--weston|--kwin|--hostwayland) ;;
*)
$Message "${1:-} does not support hardware acceleration (option --gpu)."
Return=1
;;
esac
}
Return="${Return:-"0"}"
debugnote "Dependency check for ${1:-}: $Return"
[ "$Return" = "1" ] && {
check_fallback
Autochooseserver="yes"
}
Lastcheckedxserver="${1:-}"
Lastcheckedxserverresult="$Return"
return "$Return"
}
check_xpraoption() { # check if xpra option $1 is available
local Option
Option="$(cut -d= -f1 <<< "${1:-}")"
grep -q "noprobe" <<< "${1:-}" && {
grep "OpenGL" <<< "$Xprahelp" | grep -q "probe" && echo "$@" || {
debugnote "Xpra option $@ not supported: $Option"
return 1
}
return 0
}
grep -q -- "$Option" <<< "$Xprahelp" && echo "$@" || {
debugnote "Xpra option not found: $Option"
return 1
}
}
check_xserver() { # check chosen X server, auto-choose X server
## default option '--auto': Try to automatically choose best matching and available X server
[ "$Autochooseserver" = "yes" ] && { Xserver="--xpra"
[ "$Sharegpu" = "yes" ] && Xserver="--xpra-xwayland"
[ "$Xfishtank" = "yes" ] && Xserver="--xephyr"
[ "$Desktopmode" = "yes" ] && Xserver="--xephyr"
[ "$Xserver" = "--xephyr" ] && { check_xdepends --xephyr || Xserver="--weston-xwayland" ; } ### FIXME: don't use check_xdepends() here
[ "$Sharegpu" = "yes" ] && [ "$Xserver" = "--xephyr" ] && Xserver="--weston-xwayland"
[ "$Outputcount" != "1" ] && Xserver="--weston-xwayland"
[ -n "$Rotation" ] && Xserver="--weston-xwayland"
[ "$Scaling" ] && [ "$Sharegpu" = "yes" ] && Xserver="--xpra-xwayland"
[ "$Scaling" ] && [ "$Sharegpu" = "no" ] && Xserver="--xpra"
[ "$Runsonconsole" = "yes" ] && Xserver="--xorg"
[ -z "$Hostdisplay" ] && [ -n "$Hostwaylandsocket" ] && Xserver="--xpra"
[ "$Winsubsystem" ] && Xserver="--runx"
[ "$Winsubsystem" = "CYGWIN" ] && Xserver="--xwin"
[ "$Setupwayland" = "yes" ] && { [ -n "$Hostwaylandsocket" ] && [ "$Desktopmode" = "no" ] && Xserver="--hostwayland" || Xserver="--weston" ; }
}
[ "$Sharegpu" = "yes" ] && {
[ "$Runtime" = "kata-runtime" ] && {
note "Option --gpu: Hardware acceleration with --runtime=kata-runtime
is not supported. Fallback: Disabling option --gpu."
check_fallback
Sharegpu="no"
}
case $Xserver in
--xpra)
note "Option --xpra does not support GPU access.
Fallback: Will try to use option --xpra-xwayland."
check_fallback
Xserver="--xpra-xwayland"
;;
--xephyr)
note "Option --xephyr does not support GPU access.
Fallback: Will try to use option --weston-xwayland."
check_fallback
Xserver="--weston-xwayland"
;;
--nxagent)
case "$Desktopmode" in
yes) Xserver="--weston-xwayland" ;;
no) Xserver="--xpra-xwayland" ;;
esac
note "Option --nxagent does not support GPU access.
Fallback: Will try to use option $Xserver."
check_fallback
;;
--xdummy|--xvfb)
note "Using special setup with Weston, Xwayland and xdotool
instead of Xdummy or Xvfb to allow GPU access."
Xserver="--xdummy-xwayland"
;;
esac
}
grep -q -i "GNOME" <<< "$XDG_CURRENT_DESKTOP" && {
Gnomeversion="$(gnome-shell --version)"
[ "$Gnomeversion" ] && verlt "$Gnomeversion" "GNOME Shell 3.38" && {
case $Xserver in
--hostdisplay) ;;
*)
warning "You are running GNOME desktop in outdated version
$Gnomeversion
This might cause issues with host applications if using additional X servers.
It is recommended to use another desktop environment or GNOME >= 3.38.
Only otherwise discouraged option --hostdisplay might work as expected."
case $Autochooseserver in
yes) Xserver="--hostdisplay" ;;
esac
;;
esac
}
}
# X over TCP
[ -z "$Xoverip" ] && {
[ "$Runtime" = "kata-runtime" ] && Xoverip="yes"
[ "$Runsinsnap" = "yes" ] && Xoverip="yes"
case $Mobyvm in
yes) Xoverip="yes" ;;
esac
[ "$Xoverip" = "yes" ] && [ "$Autochooseserver" = "no" ] && note "Enabled X over TCP instead of sharing unix socket."
}
[ "$Nvidiaversion" ] && [ "$Sharegpu" = "yes" ] && case $Xserver in
--xpra-xwayland|--weston-xwayland|--xwayland|--weston|--kwin|--kwin-xwayland|--xdummy-xwayland|--hostwayland)
note "Your system uses closed source NVIDIA driver.
GPU support will work only with options --hostdisplay and --xorg.
Consider to use free open source nouveau driver instead."
;;
esac
[ "$Runsonconsole" = "no" ] && [ "$Runsoverssh" = "no" ] && [ -z "$Hostdisplay$Hostwaylandsocket" ] && [ "$Xserver" != "--tty" ] && [ -z "$Winsubsystem" ] && {
warning "Environment variables DISPLAY and WAYLAND_DISPLAY are empty,
but it seems x11docker was started within X, not from console.
Please set DISPLAY and XAUTHORITY.
If you have started x11docker with su or sudo, su/sudo may be configured to
unset X environment variables. It may work if you run x11docker with
sudo -E x11docker [...]
If your system does not support 'sudo -E', you can try
sudo env DISPLAY=\$DISPLAY XAUTHORITY=\$XAUTHORITY x11docker [...]
Otherwise, you can use tools like gksu/gksudo/kdesu/kdesudo/lxsu/lxsudo."
[ -n "${PKEXEC_UID:-}" ] && note "It seems you have started x11docker with pkexec.
Can not determine DISPLAY and XAUTHORITY, can not use your X server.
To allow other X server options, please provide environment variables with
pkexec env DISPLAY=\$DISPLAY XAUTHORITY=\$XAUTHORITY x11docker [ARGS]."
[ "$Autochooseserver" = "yes" ] && Xserver="--xorg"
}
[ "$Runsoverssh" = "yes" ] && [ -z "$Hostdisplay$Hostwaylandsocket" ] && [ "$Xserver" != "--tty" ] && [ "$Autochooseserver" = "yes" ] && {
error "You are running x11docker over SSH without providing a display.
Please run with 'ssh -X' or 'ssh -Y'.
(If you insist, you can run with option '--xorg', but won't see the result remotely.)"
}
## check if dependencies for chosen X server are installed, fall back to best alternatives if not
[ "$Xserver" = "--xwin" ] && { check_xdepends --xwin || Xserver="--runx" ; }
[ "$Xserver" = "--runx" ] && { check_xdepends --runx || Xserver="--hostdisplay" ; }
[ "$Xserver" = "--hostdisplay" ] && { check_xdepends --hostdisplay || Xserver="--xpra" ; }
[ "$Xserver" = "--xephyr" ] && { check_xdepends --xephyr || Xserver="--nxagent" ; }
[ "$Xserver" = "--xvfb" ] && { check_xdepends --xvfb || Xserver="--xdummy" ; }
[ "$Xserver" = "--hostwayland" ] && { check_xdepends --hostwayland || Xserver="--weston" ; }
[ "$Xserver" = "--nxagent" ] && { check_xdepends --nxagent || { [ "$Desktopmode" = "yes" ] && Xserver="--xephyr" || Xserver="--xpra" ; } ; }
[ "$Xserver" = "--xpra" ] && { check_xdepends --xpra || { [ -z "$Hostdisplay" ] && Xserver="--weston-xwayland" ; } ; }
[ "$Xserver" = "--xpra" ] && { check_xdepends --xpra || { check_xdepends --nxagent && Xserver="--nxagent" || Xserver="--xephyr" ; } ; }
[ "$Xserver" = "--xorg" ] && { check_xdepends --xorg || Xserver="--weston-xwayland" ; }
[ "$Xserver" = "--xpra-xwayland" ] && { check_xdepends --xpra-xwayland || Xserver="--weston-xwayland" ; }
[ "$Xserver" = "--xwayland" ] && { check_xdepends --xwayland || Xserver="--weston-xwayland" ; }
[ "$Xserver" = "--xpra-xwayland" ] && { check_xdepends --xpra-xwayland || { [ "$Desktopmode" = "yes" ] && Xserver="--kwin-xwayland" || Xserver="--hostdisplay" ; } ; }
[ "$Xserver" = "--kwin-xwayland" ] && { check_xdepends --kwin-xwayland || Xserver="--weston-xwayland" ; }
[ "$Xserver" = "--kwin" ] && { check_xdepends --kwin || Xserver="--weston" ; }
[ "$Xserver" = "--weston-xwayland" ] && { check_xdepends --weston-xwayland || Xserver="--kwin-xwayland" ; }
[ "$Xserver" = "--weston" ] && { check_xdepends --weston || Xserver="--kwin" ; }
[ "$Xserver" = "--xdummy-xwayland" ] && { check_xdepends --xdummy-xwayland || Xserver="--kwin-xwayland" ; }
case $Xserver in
--weston|--kwin|--hostwayland) Setupwayland="yes" ;;
esac
[ "$Setupwayland" = "yes" ] && { check_xdepends $Xserver || error "Failed to set up a Wayland environment.
Please install 'weston' or 'kwin_wayland'.
Wayland is not possible with proprietary NVIDIA driver
or with --runtime=kata-runtime." ; }
# Xephyr as fallback for all options. Last fallback: Xorg
check_xdepends $Xserver || Xserver="--xephyr"
[ "$Xserver" = "--xephyr" ] && { check_xdepends --xephyr || {
check_xdepends --kwin-xwayland && Xserver="--kwin-xwayland"
check_xdepends --hostdisplay && [ "$Desktopmode" = "no" ] && Xserver="--hostdisplay"
check_xdepends --runx && Xserver="--runx"
check_xdepends --xwin && Xserver="--xwin"
check_xdepends --nxagent && Xserver="--nxagent"
check_xdepends --weston-xwayland && Xserver="--weston-xwayland"
check_xdepends --xpra && Xserver="--xpra"
}
[ "$Sharegpu" = "yes" ] && case $Desktopmode in
yes) check_xdepends --weston-xwayland && Xserver="--weston-xwayland" ;;
no) check_xdepends --hostdisplay && Xserver="--hostdisplay" ;;
esac
[ "$Runsonconsole" = "yes" ] && {
check_xdepends --kwin-xwayland && Xserver="--kwin-xwayland"
check_xdepends --weston-xwayland && Xserver="--weston-xwayland"
check_xdepends --xorg && Xserver="--xorg"
}
check_xdepends $Xserver || Xserver="--xorg"
}
check_xdepends $Xserver || {
case $Winsubsystem in
"")
error "Did not find a possibility to provide a display.
Recommendations:
To run within an already running X server, install one or all of:
Xephyr xpra nxagent
To run with GPU acceleration, install:
weston and Xwayland, optionally also: xpra and xdotool
To run from TTY or within Wayland, install:
weston and Xwayland
$Wikipackages"
;;
CYGWIN)
error "Did not find a possibility to provide a display.
Please install packages 'xinit' and 'xauth' in Cygwin,
or run x11docker with --runx: https://github.com/mviereck/runx"
;;
MSYS2|WSL1|WSL2)
[ "$Hostdisplay" ] && {
error "Did not find a possibility to provide a nested display.
Please install package 'xinit' and one or all of: nxagent Xephyr xpra
$Wikipackages"
} || {
error "Did not find a possibility to provide a display.
Please use --runx to provide an X server on MS Windows:
https://github.com/mviereck/runx"
}
;;
esac
}
[ "$Autochooseserver" = "yes" ] && note "Using X server option $Xserver"
storeinfo "xserver=$Xserver"
return 0
}
create_xcommand() { ### create command to start X server and/or Wayland compositor
local Xserveroptions_custom= Xpraoptions= Nxagentoptions= Compositorpid= Weston= Westonoutput= Count= Envvar=
Xserveroptions_custom="$Xserveroptions"
Xserveroptions=""
#### General X server options
case $Xserver in
--nxagent)
{ [ "$Sharehostipc" = "yes" ] || [ "$X11dockermode" = "exe" ] ; } && {
Xserveroptions="$Xserveroptions \\
-shmem \\
-shpix"
} || {
Xserveroptions="$Xserveroptions \\
-noshmem \\
-noshpix"
}
;;
*) Xserveroptions=" \\
-retro \\
+extension RANDR \\
+extension RENDER \\
+extension GLX \\
+extension XVideo \\
+extension DOUBLE-BUFFER \\
+extension SECURITY \\
+extension DAMAGE \\
+extension X-Resource \\
-extension XINERAMA -xinerama"
case $Sharehostipc in
yes) Xserveroptions="$Xserveroptions \\
+extension MIT-SHM" ;;
no)
Xserveroptions="$Xserveroptions \\
-extension MIT-SHM"
Xprashm="XPRA_XSHM=0" ;;
esac
;;
esac
# X extension COMPOSITE
[ "$Xcomposite" ] || case $Xserver in
--nxagent|--xwin) Xcomposite="no" ;;
*) Xcomposite="yes" ;;
esac
case $Xcomposite in
yes)
# Old X servers have extension "Composite", recent ones call it "COMPOSITE".
Xserveroptions="$Xserveroptions \\
+extension Composite +extension COMPOSITE" ;;
no)
Xserveroptions="$Xserveroptions \\
-extension Composite -extension COMPOSITE" ;;
esac
# X extension XTEST
[ "$Xtest" ] || case $Xserver in
--xpra|--xpra-xwayland|--xdummy|--xdummy-xwayland|--xvfb) Xtest="yes" ;;
*) Xtest="no" ;;
esac
case "$Xtest" in
yes) Xserveroptions="$Xserveroptions \\
+extension XTEST" ;;
no) Xserveroptions="$Xserveroptions \\
-extension XTEST -tst" ;;
esac
# Disable screensaver
Xserveroptions="$Xserveroptions \\
-dpms \\
-s off"
# X cookie authentication
case $Xauthentication in
yes)
Xserveroptions="$Xserveroptions \\
-auth $Xservercookie" ;;
no)
case $Xoverip in
yes) warning "Option --no-auth: SECURITY RISK!
Allowing access to new X server for everyone.
Your X server is accessable over TCP network without any restriction.
That can be abused to take control over your system." ;;
no|"") [ "$Xserver" = "--hostdisplay" ] || warning "Option --no-auth: SECURITY RISK!
Allowing access to new X server for everyone."
Xserveroptions="$Xserveroptions \\
-ac" ;;
esac
esac
# X over IP/TCP
case $Xoverip in
yes)
case $Xserver in
--nxagent) ;;
*) Xserveroptions="$Xserveroptions \\
-listen tcp" ;;
esac
;;
no|"") Xserveroptions="$Xserveroptions \\
-nolisten tcp" ;;
esac
# check DPI
case $Xserver in
--xpra|--xpra-xwayland)
{ [ -n "$Dpi" ] || [ "$Scaling" ] ; } && verlt "$Xpraversion" "v2.1-r16547" && ! verlt "$Xpraversion" "v2.1" && {
note "Option --dpi is buggy in xpra $Xpraversion
due to xpra bug #1605. Need at least xpra v2.1-r16547 or one of 2.0 series.
This affects option --scale, too, leading to wrong font sizes.
Fallback: disabling dpi settings."
check_fallback
Dpi="-1"
} ;;
esac
case $Xserver in
--weston|--kwin|--tty|--hostdisplay) ;;
--xwin|--runx) ;;
*)
[ -z "$Dpi" ] && {
xdpyinfo >/dev/null 2>&1 && {
Dpi="$(xdpyinfo | grep dots | cut -dx -f2 | cut -d' ' -f1)"
} || {
[ -n "$Hostdisplay" ] && [ -z "$(command -v xdpyinfo)" ] && note "Could not determine dpi settings. If you encounter too big or
too small fonts with $Xserver, please install xdpyinfo or use option --dpi.
$Wikipackages"
}
case $Xserver in
--xpra|--xpra-xwayland)
[ "$Scaling" ] && [ "$Desktopmode" = "no" ] && {
Dpi="$(awk -v a="$Scaling" -v b="$Dpi" 'BEGIN {print (b * a * a)}')"
Dpi="${Dpi%.*}"
} ;;
esac
}
;;
esac
[ "$Dpi" = "-1" ] && Dpi=""
[ -n "$Dpi" ] && Xserveroptions="$Xserveroptions \\
-dpi $Dpi"
#### xpra server and client command
case $Xserver in
--xpra|--xpra-xwayland)
Xpraoptions="\\
$(check_xpraoption --csc-modules=none) \\
$(check_xpraoption --encodings=rgb) \\
$(check_xpraoption --microphone=no) \\
$(check_xpraoption --notifications=no) \\
$(check_xpraoption --pulseaudio=no) \\
$(check_xpraoption --socket-dirs="'$Cachefolder'") \\
$(check_xpraoption --speaker=no) \\
$(check_xpraoption --start-via-proxy=no) \\
$(check_xpraoption --webcam=no) \\
$(check_xpraoption --xsettings=no)"
# $(check_xpraoption --clipboard-direction=both) \\
# $(check_xpraoption --system-tray=yes) \\
# --keymap
[ "$Xkblayout" ] && Xpraoptions="$Xpraoptions \\
$(check_xpraoption --keyboard-layout="'$Xkblayout'") \\
$(check_xpraoption --keyboard-raw=yes)"
# Xpraoptions="$Xpraoptions $(check_xpraoption --debug=all)" ; Preservecachefiles="yes" # Debugging only
# xpra server command
[ "$Desktopmode" = "yes" ] && Xpraservercommand="xpra start-desktop" || Xpraservercommand="xpra start"
Xpraservercommand="$Xpraservercommand :$Newdisplaynumber --use-display $Xpraoptions \\
$(check_xpraoption --clipboard=yes)\\
$(check_xpraoption --dbus-proxy=no) \\
$(check_xpraoption --daemon=no) \\
$(check_xpraoption --fake-xinerama=no) \\
$(check_xpraoption --file-transfer=off) \\
$(check_xpraoption --html=off) \\
$(check_xpraoption --opengl=noprobe) \\
$(check_xpraoption --mdns=no) \\
$(check_xpraoption --printing=no) \\
$(check_xpraoption --session-name="'$Codename'") \\
$(check_xpraoption --start-new-commands=no) \\
$(check_xpraoption --systemd-run=no) \\
$(check_xpraoption --video-encoders=none)"
# disable --dpi for buggy versions
[ -n "$Dpi" ] && verlt "$Xpraversion" "v2.1-r16547" && ! verlt "$Xpraversion" "v2.1" && Dpi=""
[ -n "$Dpi" ] && Xpraservercommand="$Xpraservercommand \\
$(check_xpraoption --dpi="'$Dpi'")"
# xpra client command
Xpraclientcommand="xpra attach :$Newdisplaynumber $Xpraoptions \\
$(check_xpraoption --clipboard=$Shareclipboard) \\
$(check_xpraoption --compress=0) \\
$(check_xpraoption --modal-windows=no) \\
$(check_xpraoption --opengl=auto) \\
$(check_xpraoption --quality=100) \\
$(check_xpraoption --video-decoders=none)"
[ "$Fullscreen" = "yes" ] && Xpraclientcommand="$Xpraclientcommand \\
$(check_xpraoption --desktop-fullscreen=yes)"
[ "$Scaling" ] && Xpraclientcommand="$Xpraclientcommand \\
$(check_xpraoption --desktop-scaling="'$Scaling'")"
# [ -n "$Dpi" ] && Xpraclientcommand="$Xpraclientcommand \\
# $(check_xpraoption --dpi="'$Dpi'")"
[ "$Xpraborder" ] && Xpraclientcommand="$Xpraclientcommand \\
$(check_xpraoption --border="$Xpraborder")"
[ "$X11dockermode" = "run" ] && {
case "$Desktopmode" in
yes) Xpraclientcommand="$Xpraclientcommand \\
$(check_xpraoption --title="'$Codename on $Newdisplay [in container] (shift+F11 toggles fullscreen)'")" ;;
no) Xpraclientcommand="$Xpraclientcommand \\
$(check_xpraoption --title="'@title@ [in container]'")" ;;
esac
}
# xpra environment variables
for Line in $Xpracontainerenv; do
store_runoption env "$Line"
done
;;
esac
#### Prepare weston.ini: config file for Weston
case $Xserver in
--weston|--weston-xwayland|--xpra-xwayland|--xdummy-xwayland)
command -v weston-launch >/dev/null && [ "$Runsonconsole" = "yes" ] && [ "$Runsinteractive" = "yes" ] && Weston="weston-launch -v --" || Weston="weston"
echo "[core]
shell=desktop-shell.so
idle-time=0
[shell]
panel-location=none
panel-position=none
locking=false
background-color=0xff002244
animation=fade
startup-animation=fade
[keyboard]
" >> "$Westonini"
# --keymap: keyboard layout
[ -n "$Xkblayout" ] && echo "keymap_layout=$Xkblayout" >> "$Westonini"
[ -z "$Xkblayout" ] && [ "$Runsonconsole" = "yes" ] && echo "$(echo -n "keymap_layout=" && grep XKBLAYOUT <"/etc/default/keyboard" | cut -d= -f2 | cut -d'"' -f2)" >> "$Westonini"
case $Runsonconsole in
no) # Display prefix X or WL; needed to indicate if host Wayland or host X provides the nested window.
#[ -n "$Hostwaylandsocket" ] && [ "$Xserver" != "--xpra-xwayland" ] && [ "$Hostsystem" != "ubuntu" ] && [ "$Fullscreen" = "no" ] && Westonoutput="WL"
[ -n "$Hostdisplay" ] && Westonoutput="X"
[ -z "$Westonoutput" ] && [ -n "$Hostwaylandsocket" ] && Westonoutput="WL"
;;
yes) # short start&stop of Weston to grep name of monitor. Needed instead of WL or X prefix
[ -n "$Screensize" ] || [ "$Scaling" ] || [ -n "$Rotation" ] && {
Westonoutput="$(weston_getoutputname)"
debugnote "$Xserver: Detected screen output for weston: $Westonoutput"
}
;;
esac
;;
esac
#### create command to run X server
case $Xserver in
--xorg)
Xserveroptions="$Xserveroptions \\
-verbose"
[ "$Xorgconf" ] && Xserveroptions="$Xserveroptions \\
-config '$Xorgconf'" # --xorgconf
[ "$Runsonconsole" = "yes" ] && Xserveroptions="$Xserveroptions \\
-keeptty"
Xcommand="$(command -v Xorg) :$Newdisplaynumber vt$Newxvt $Xserveroptions"
;;
--xpra)
case $Xpravfb in
Xvfb) Xcommand="$(command -v Xvfb) :$Newdisplaynumber $Xserveroptions \\
-screen 0 ${Maxxaxis}x${Maxyaxis}x24" ;;
Xdummy) Xcommand="$Xorgwrapper :$Newdisplaynumber $Xserveroptions \\
-config $Xdummyconf \\
vt128" ;;
esac
;;
--xdummy)
Xcommand="$Xorgwrapper :$Newdisplaynumber vt128 $Xserveroptions \\
-config $Xdummyconf"
;;
--xvfb)
Xcommand="$(command -v Xvfb) :$Newdisplaynumber $Xserveroptions \\
-screen 0 ${Screensize}x24" ### FIXME: hardcoded setting of depth 24. Could be better?
;;
--xephyr)
command -v Xephyr >/dev/null && {
Xserveroptions="$Xserveroptions \\
-resizeable \\
-noxv"
# Xserveroptions="$Xserveroptions \\
# -glamor" # disabled because of lagginess reported in #196
case $Fullscreen in
yes)
Xserveroptions="$Xserveroptions \\
-fullscreen"
;;
no)
grep -q -- "-output " <<< "$Xserveroptions_custom" || Xserveroptions="$Xserveroptions \\
-screen $Screensize"
;;
esac
Xcommand="$(command -v Xephyr) :$Newdisplaynumber $Xserveroptions"
} || {
# Fallback: Xnest
Xcommand="$(command -v Xnest) :$Newdisplaynumber $Xserveroptions \\
-geometry $Screensize \\
-scrns $Outputcount \\
-name '$Codename on $Newdisplay' "
note "Option --xephyr: Xephyr not found. Fallback: using Xnest.
Xnest is less stable and has less features than Xephyr.
For example, it misses RandR and Composite extensions and fullscreen mode.
It is recommended to install 'Xephyr'.
$Wikipackages"
check_fallback
}
;;
--xwayland)
Xcommand="$(command -v Xwayland) :$Newdisplaynumber $Xserveroptions"
;;
--xpra-xwayland|--xdummy-xwayland)
Xcommand="$(command -v Xwayland) :$Newdisplaynumber $Xserveroptions"
echo "[output]
name=${Westonoutput}1
mode=$Screensize" >> $Westonini
[ -n "$Customwestonini" ] && Westonini="$Customwestonini"
Compositorcommand="$Weston \\
--socket=$Newwaylandsocket \\
--backend=x11-backend.so \\
--config='$Westonini'"
[ "$Xserver" = "--xpra-xwayland" ] && case $Scaling in
"") Compositorcommand="$Compositorcommand \\
--fullscreen" ;;
*) Compositorcommand="$Compositorcommand \\
--width=$(cut -dx -f1 <<< "$Screensize") --height=$(cut -dx -f2 <<< "$Screensize")" ;;
esac
;;
--weston|--weston-xwayland)
Xcommand="$(command -v Xwayland) :$Newdisplaynumber $Xserveroptions"
[ -n "$Westonoutput" ] && for ((Count=1 ; Count<="$Outputcount" ; Count++)) ; do
[ "$Westonoutput" = "WL" ] || [ "$Westonoutput" = "X" ] || {
Count=""
[ -z "$Screensize" ] && Screensize="preferred"
}
echo "[output]
name=$Westonoutput$Count
mode=$Screensize
" >> $Westonini
[ "$Scaling" ] && echo "scale=$Scaling" >> $Westonini
[ -n "$Rotation" ] && echo "transform=$Rotation" >> $Westonini
[ "$Count" ] || break
done
Compositorcommand="$Weston \\
--socket=$Newwaylandsocket"
[ "$Fullscreen" = "yes" ] && Compositorcommand="$Compositorcommand \\
--fullscreen"
[ "$Outputcount" = "1" ] || Compositorcommand="$Compositorcommand \\
--output-count=$Outputcount"
case $Westonoutput in
WL) Compositorcommand="$Compositorcommand \\
--backend=wayland-backend.so" ;;
X) Compositorcommand="$Compositorcommand \\
--backend=x11-backend.so" ;;
*)
case "$Runsonconsole" in
yes) Compositorcommand="$Compositorcommand \\
--backend=drm-backend.so" ;;
no) Compositorcommand="$Compositorcommand \\
--backend=x11-backend.so" ;;
esac
;;
esac
[ -n "$Customwestonini" ] && Westonini="$Customwestonini"
Compositorcommand="$Compositorcommand \\
--config='$Westonini'"
;;
--kwin|--kwin-xwayland)
Xcommand="$(command -v Xwayland) :$Newdisplaynumber $Xserveroptions"
Compositorcommand="kwin_wayland \\
--xwayland \\
--socket=$Newwaylandsocket \\
--width=$Xaxis --height=$Yaxis"
[ "$Outputcount" = "1" ] || Compositorcommand="$Compositorcommand \\
--output-count=$Outputcount"
[ "$Xkblayout" ] && Compositorcommand="KWIN_XKB_DEFAULT_KEYMAP=$Xkblayout $Compositorcommand"
Compositorcommand="env QT_XKB_CONFIG_ROOT=/usr/share/X11/xkb $Compositorcommand"
case $Runsonconsole in
yes) Compositorcommand="$Compositorcommand \\
--drm" ;;
no)
kwin_wayland --help | grep -q -- '--windowed' && {
Compositorcommand="$Compositorcommand \\
--windowed"
} || {
kwin_wayland --help | grep -q -- '--x11-display' && [ "$Hostdisplay" ] && Compositorcommand="$Compositorcommand \\
--x11-display=$Hostdisplay"
};;
esac
;;
--nxagent)
# files needed by nxagent
export NXAGENT_KEYSTROKEFILE="$Nxagentkeysfile"
export NX_CLIENT="$Nxagentclientrc"
Xserveroptions="$Xserveroptions \\
-norootlessexit \\
-ac \\
-options $Nxagentoptionsfile \\
-keystrokefile $NXAGENT_KEYSTROKEFILE"
case $Desktopmode in
"yes") Xserveroptions="$Xserveroptions \\
-D \\
-name '$Imagename on $Newdisplay (shift+F11 toggles fullscreen)'" ;;
"no") Xserveroptions="$Xserveroptions \\
-R" ;;
esac
Xcommand="$(command -v nxagent) :$Newdisplaynumber $Xserveroptions"
# Some additional nxagent options are stored in a file
Nxagentoptions="nx/nx"
[ "$Shareclipboard" = "yes" ] && Nxagentoptions="$Nxagentoptions,clipboard=both" || Nxagentoptions="$Nxagentoptions,clipboard=none"
case $Fullscreen in
yes) Nxagentoptions="$Nxagentoptions,fullscreen=1" ;;
no) [ -n "$Screensize" ] && Nxagentoptions="$Nxagentoptions,geometry=$Screensize" ;;
esac
# set keyboard layout
case $Xkblayout in
"") # set layout from host.
command -v setxkbmap >/dev/null && {
Nxagentoptions="$Nxagentoptions,keyboard=$(setxkbmap -query | grep rules | awk '{print $2}')/$(setxkbmap -query | grep layout | awk '{print $2}')"
} || note "Could not check your keyboard layout due to missing setxkbmap
If you get mismatching keys, please install setxkbmap.
$Wikipackages"
;;
*) # --keymap
case $Xkblayout in
clone) Nxagentoptions="$Nxagentoptions,keyboard='clone'" ;;
*) Nxagentoptions="$Nxagentoptions,keyboard='evdev/$Xkblayout'" ;;
esac
;;
esac
Nxagentoptions="${Nxagentoptions}:${Newdisplaynumber}"
echo "$Nxagentoptions" >> "$Nxagentoptionsfile"
debugnote "$Xserver: Additional nxagent options: $Nxagentoptions"
# Workaround as nxagent ignores XAUTHORITY and fails to start if option -auth is given without containing the cookie from host display.
# Option -ac above complies "xhost +" and is reverted in xinitrc.
[ "$Xauthentication" = "yes" ] && unpriv "cp '$Hostxauthority' '$Xservercookie'"
# fake NXclient
echo '#! /usr/bin/env bash
# helper script to terminate nxagent.
# nxagent runs program noted in NX_CLIENT if window close button is pressed.
# (real nxclient does not exist)
echo "NXclient: $*" >> '$Xinitlogfile'
parsed="$(getopt --options="" --longoptions="parent:,display:,dialog:,caption:,window:,message:" -- "$@")"
eval set -- $parsed
while [ -n "${1:-}" ] ; do
case "${1:-}" in
--dialog) dialog="${2:-}" && shift ;;
--display|--caption|--message) shift ;;
--window) shift ;;
--parent) pid="${2:-}" && shift ;;
--) ;;
esac
shift
done
case $dialog in
pulldown) ;;
yesnosuspend)
kill $pid
echo timetosaygoodbye >> '$Timetosaygoodbyefile'
;;
esac
' >> "$NX_CLIENT"
unpriv "chmod +x '$NX_CLIENT'"
echo '<!DOCTYPE NXKeystroke>
<keystrokes>
<keystroke action="fullscreen" AltMeta="0" Control="0" Shift="1" key="F11" />
<keystroke action="fullscreen" AltMeta="1" Control="1" Shift="1" key="f" />
</keystrokes>' >> "$NXAGENT_KEYSTROKEFILE"
;;
--xwin)
case $Sharegpu in
no)
Iglx="no"
Xserveroptions="$Xserveroptions \\
-nowgl" ;;
yes)
Iglx="yes"
Xserveroptions="$Xserveroptions \\
-wgl" ;;
esac
case $Fullscreen in
yes)
Xserveroptions="$Xserveroptions \\
-fullscreen" ;;
no)
Xserveroptions="$Xserveroptions \\
-lesspointer"
case $Desktopmode in
yes)
for ((Count=0 ; Count<$Outputcount ; Count++)); do
Xserveroptions="$Xserveroptions \\
-screen $Count $Screensize"
done
;;
no) Xserveroptions="$Xserveroptions \\
-multiwindow" ;;
esac
;;
esac
case $Shareclipboard in
yes) Xserveroptions="$Xserveroptions \\
-clipboard" ;;
no) Xserveroptions="$Xserveroptions \\
-noclipboard" ;;
esac
Xcommand="$(command -v XWin) :$Newdisplaynumber $Xserveroptions"
;;
--runx)
Xserveroptions="--display $Newdisplaynumber \
--verbose"
[ "$Xauthentication" = "no" ] && Xserveroptions="$Xserveroptions \
--no-auth"
[ "$Desktopmode" = "yes" ] && Xserveroptions="$Xserveroptions \
--desktop"
[ "$Shareclipboard" = "yes" ] && Xserveroptions="$Xserveroptions \
--clipboard"
[ "$Screensize" ] && Xserveroptions="$Xserveroptions \
--size=$Screensize"
[ "$Sharegpu" = "yes" ] && {
Xserveroptions="$Xserveroptions \
--gpu"
store_runoption env "LIBGL_ALWAYS_INDIRECT=1"
}
Xcommand="$(command -v runx) $Xserveroptions"
;;
--hostwayland|--hostdisplay|--tty) ;;
esac
case $Xserver in
--tty|--hostdisplay|--runx) ;;
--weston|--kwin|--hostwayland) ;;
*)
case "$Iglx" in
yes) Xcommand="$Xcommand \\
+iglx"
store_runoption env "LIBGL_ALWAYS_INDIRECT=1" ;;
no)
Xcommand="$Xcommand \\
-iglx" ;;
esac
;;
esac
# --xopt
Xcommand="$Xcommand \\
$Xserveroptions_custom"
case $Xserver in
--weston|--kwin|--hostwayland|--hostdisplay|--tty) Xcommand="" ;;
esac
case $Xserver in
--weston|--kwin|--weston-xwayland|--kwin-xwayland|--xpra-xwayland|--xdummy-xwayland) ;;
*) Compositorcommand="" ;;
esac
return 0
}
disable_xhost() { # remove any access to X server granted by xhost
local Line=
command -v xhost >/dev/null || {
warning "Command 'xhost' not found.
Can not check for possibly allowed network access to X.
Please install 'xhost'."
return 1
}
xhost 2>&1 | tail -n +2 /dev/stdin | while read -r Line ; do # read all but the first line (header)
debugnote "xhost: Removing entry $Line"
xhost -$Line # disable every entry
done
xhost - # enable access control
[ "$(xhost 2>&1 | wc -l)" -gt "1" ] && {
warning "Remaining xhost permissions found on display ${DISPLAY:-}
$(xhost 2>&1 )"
return 1
}
xhost 2>&1 | grep "access control disabled" && {
warning "Failed to restrict xhost permissions.
Access to display ${DISPLAY:-} is allowed for everyone."
return 1
}
return 0
}
weston_getoutputname() { # short startup of weston on tty to grep output name
unpriv "$Weston --no-config --backend=drm-backend.so >> $Compositorlogfile 2>&1 & echo compositorpid=\$! >>$Storeinfofile"
waitforlogentry "weston-screencheck" "$Compositorlogfile" "connector" "$Compositorerrorcodes"
grep Output <$Compositorlogfile | grep connector | head -n1 | cut -d ' ' -f3 | rev | cut -c2- | rev
termpid "$(storeinfo dump compositorpid)" weston
storeinfo drop compositorpid
mkfile "$Compositorlogfile"
}
#### X helper scripts
create_modelinefile() { # generate a set of smaller modelines for screen size $1 and store them in a cache file
local Newmodelinefile Modeline Size X Y Xcount Ycount
Size="${1:-}"
X="$(echo "$Size" | cut -dx -f1)"
Y="$(echo "$Size" | cut -dx -f2)"
Newmodelinefile="$Modelinefile.$Size"
[ -e "$Newmodelinefile" ] || {
debugnote "$Xserver: Generating modelines for $Size"
mkfile "$Newmodelinefile"
for Ycount in 25 30 40 45 50 55 60 65 70 75 80 85 90 95 100; do
for Xcount in 25 30 40 45 50 55 60 65 70 75 80 85 90 95 100; do
Modeline="$(cvt "$(awk -v a="$X" -v b=$Xcount 'BEGIN {print (a * b / 100)}')" "$(awk -v a="$Y" -v b=$Ycount 'BEGIN {print (a * b / 100)}' )" | tail -n1)"
Modeline="$(echo "$Modeline" | sed s/_60.00//g)"
echo "$Modeline" >> "$Newmodelinefile"
done
done
}
echo "$Newmodelinefile"
}
create_xdummywrapper() { # options --xdummy, --xpra: create startscript for Xdummy
echo '#!/bin/sh
# fork of https://xpra.org/trac/browser/xpra/trunk/src/scripts/xpra_Xdummy
find_ld_linux() {
arch="$(uname -m)"
if [ $arch = "x86_64" ]; then
LD_LINUX="/lib64/ld-linux-x86-64.so.2"
elif [ $arch = "i386" ]; then
LD_LINUX="/lib/ld-linux.so.2"
elif [ $arch = "i486" ]; then
LD_LINUX="/lib/ld-linux.so.2"
elif [ $arch = "i586" ]; then
LD_LINUX="/lib/ld-linux.so.2"
elif [ $arch = "i686" ]; then
LD_LINUX="/lib/ld-linux.so.2"
elif [ $arch = "armel" ]; then
LD_LINUX="/lib/ld-linux.so.3"
elif [ $arch = "armhfp" ]; then
LD_LINUX="/lib/ld-linux.so.3"
elif [ $arch = "armhf" ]; then
LD_LINUX="/lib/ld-linux-armhf.so.3"
elif [ $arch = "ppc64" ]; then
LD_LINUX="/lib64/ld64.so.1"
elif [ $arch = "s390x" ]; then
LD_LINUX="/lib64/ld64.so.1"
else
#suitable for: powerpc/ppc, mips/mipsel, s390 and others:
LD_LINUX="/lib/ld.so.1"
fi
if [ ! -x "$LD_LINUX" ]; then
# Musl C / Alpine Linux
ldmusl="$(ls /lib | grep ^ld-musl)"
if [ -n "$ldmusl" ]; then
LD_LINUX="/lib/$ldmusl"
else
LD_LINUX=""
echo "could not determine ld path for $arch, please file an xpra bug"
fi
fi
}
if [ -x "/usr/libexec/Xorg" ]; then
#Fedora 22+ workaround where /usr/bin/Xorg is not suid
#because it is a script, command -v calls /usr/libexec/Xorg.wrap
#command -v is setuid, and command -v eventually calls this one:
XORG_BIN="/usr/libexec/Xorg"
elif [ -x "/usr/libexec/Xorg.bin" ]; then
#Fedora 21 workaround where /usr/bin/Xorg is not suid
#because it is a script, command -v calls /usr/libexec/Xorg.wrap
#command -v is setuid, and command -v eventually calls this one:
XORG_BIN="/usr/libexec/Xorg.bin"
elif [ -x "/usr/lib/xorg-server/Xorg" ]; then
#Arch Linux:
exec "/usr/lib/xorg-server/Xorg" "$@"
elif [ -x "/usr/lib/xorg/Xorg" ]; then
#Ubuntu 16.10:
exec "/usr/lib/xorg/Xorg" "$@"
else
XORG_BIN="$(command -v Xorg)"
fi
if [ ! -x "$XORG_BIN" ]; then
echo "failed to locate Xorg binary to run"
exit 1
fi
if [ -u "$XORG_BIN" ]; then
# setuid is set, we need to do magic
find_ld_linux
if [ -n "$LD_LINUX" ]; then
if [ -n "$BASH" ]; then
#running in bash, can show a more helpful command name:
exec -a "Xorg-nosuid" "$LD_LINUX" "$XORG_BIN" "$@"
else
exec "$LD_LINUX" "$XORG_BIN" "$@"
fi
else
#fallback to making a copy of the binary:
DOTXPRA_DIR="$HOME/.xpra"
if [ ! -d "$DOTXPRA_DIR" ]; then
mkdir "$DOTXPRA_DIR"
chmod 700 "$DOTXPRA_DIR"
fi
NOSUID_XORG="$DOTXPRA_DIR/Xorg-nosuid"
cp -f "$XORG_BIN" "$NOSUID_XORG"
exec "$NOSUID_XORG" "$@"
fi
else
# setuid is not set on xorg_bin
exec "$XORG_BIN" "$@"
fi
'
} >> $Xorgwrapper
create_xdummyxorgconf() { # options --xdummy, --xpra: create xorg.conf for Xdummy
local Modelinefile
echo '# This xorg configuration file is forked and changed from xpra to start a dummy X11 server.
# For original and details, please see: https://xpra.org/Xdummy.html
# Set of modelines for different resolutions is created in xinitrc.
Section "ServerFlags"
Option "DontVTSwitch" "true"
Option "AllowMouseOpenFail" "true"
Option "PciForceNone" "true"
Option "AutoEnableDevices" "false"
Option "AutoAddDevices" "false"
EndSection
Section "Device"
Identifier "dummy_videocard"
Driver "dummy"
DacSpeed 600
Option "ConstantDPI" "true"
VideoRam '$(($Maxxaxis * $Maxyaxis * 2 * 32 / 8 / 1024))'
EndSection
Section "Monitor"
Identifier "dummy_monitor"
HorizSync 1.0 - 2000.0
VertRefresh 1.0 - 200.0
' >> $Xdummyconf
# Modeline for desired virtual screen size
echo "Modeline $Modeline" >> $Xdummyconf
# Subset of smaller Modelines
Modelinefile="$(create_modelinefile "${Maxxaxis}x${Maxyaxis}")"
cat "$Modelinefile" >> $Xdummyconf
echo '
EndSection
Section "Screen"
Identifier "dummy_screen"
Device "dummy_videocard"
Monitor "dummy_monitor"
DefaultDepth 24
SubSection "Display"
Viewport 0 0
Depth 32
Modes '$(echo $Modeline | cut -d " " -f1)'
Virtual '$Xaxis' '$Yaxis'
EndSubSection
EndSection
Section "ServerLayout"
Identifier "dummy_layout"
Screen "dummy_screen"
EndSection
' >> $Xdummyconf
}
create_xinitrc() { # create xinitrc: set up X environment, create cookies
echo "#! /bin/sh"
#[ "$Debugmode" = "yes" ] && echo "set -x"
declare -f disable_xhost
declare -f pspid
declare -f rocknroll
declare -f storeinfo
declare -f storepid
echo "$Messagefifofuncs"
echo "getscreensize() {"
echo " CurrentXaxis=\"\$(xrandr | grep primary | cut -d' ' -f4 | cut -dx -f1 )\""
echo " CurrentYaxis=\"\$(xrandr | grep primary | cut -d' ' -f4 | cut -dx -f2 | cut -d+ -f1)\""
echo "}"
echo "checkscreensize() {"
echo " getscreensize"
echo " [ \"\$Xaxis\" = \"\$CurrentXaxis\" ] || return 1"
echo " [ \"\$Yaxis\" = \"\$CurrentYaxis\" ] || return 1"
echo " return 0"
echo "}"
echo "getprimary() {"
echo " xrandr | grep -q primary || xrandr --output \$(xrandr | grep ' connected' | head -n1 | cut -d' ' -f1) --primary"
echo " echo \$(xrandr | grep primary | cut -d' ' -f1)"
echo "}"
echo ""
echo "Messagefile='$Messagefifo'"
echo "Output=\"\$(getprimary)\""
echo "Storeinfofile='$Storeinfofile'"
echo "Storepidfile='$Storepidfile'"
echo "Timetosaygoodbyefile='$Timetosaygoodbyefile'"
echo ""
echo "export PATH='${PATH:-}'"
echo ""
echo "Cookie=''"
echo "Line=''"
echo "Var=''"
echo ""
echo "debugnote 'Running xinitrc'"
echo ""
case $Xserver in
--weston|--kwin|--hostwayland)
echo "export $Newxenv"
echo "unset DISPLAY XAUTHORITY"
echo "export DISPLAY XAUTHORITY"
;;
--tty)
echo "unset DISPLAY XAUTHORITY WAYLAND_DISPLAY"
echo "export DISPLAY XAUTHORITY WAYLAND_DISPLAY"
;;
--runx)
[ "$Xauthentication" = "yes" ] && {
echo "# cookie generated by runx"
echo 'debugnote "xinitrc: Option --runx: Using cookie: $XAUTHORITY"'
echo "cp -T \"\$XAUTHORITY\" '$Xclientcookie'"
echo "cp -T \"\$XAUTHORITY\" '$Xservercookie'"
echo "debugnote \"xinitrc: Cookie: \$(xauth -f $Xclientcookie list 2>&1)\""
}
echo "export $Newxenv"
;;
*) # here something for real X servers
echo "export $Newxenv"
echo "# background color"
case $Xserver in
--hostdisplay) ;;
--nxagent)
echo "sleep 2 && xsetroot -solid '#7F7F7F' 2>/dev/null &" ;;
*) echo "xsetroot -solid '#7F7F7F' 2>/dev/null" ;;
esac
echo ""
[ "$Xauthentication" = "yes" ] && {
echo "# create new XAUTHORITY cookies"
echo ":> $Xclientcookie"
echo ""
[ "$Xserver" = "--nxagent" ] && {
echo "cp $Xclientcookie $Xclientcookie.bak # nxagent workaround cookie was created before starting xinit"
echo "export XAUTHORITY=$Xclientcookie.bak"
echo ""
}
[ "$Trusted" = "yes" ] && Trusted="trusted" || Trusted="untrusted"
[ "$Xserver" = "--hostdisplay" ] && {
[ -s "$Hostxauthority" ] && echo "export XAUTHORITY=$Hostxauthority"
echo "xhost | grep -q 'SI:localuser:$Hostuser' || { xhost +SI:localuser:$Hostuser ; Xhostentry='yes' ; }"
echo ""
}
case "$Runsoverssh" in
no)
echo "echo 'Requesting $Trusted cookie from X server'"
echo "xauth -v -i -f $Xclientcookie generate $Newdisplay . $Trusted timeout 3600"
;;
yes)
echo "verbose 'Can not use cookies created over SSH. Will bake one myself.'"
;;
esac
echo ""
echo "[ -s '$Xclientcookie' ] || { "
echo " [ '$Trusted' = 'untrusted' ] && note 'Could not create untrusted cookie.
Maybe your X server misses extension SECURITY.'"
[ "$Xserver" = "--hostdisplay" ] && {
[ "$Sharehostipc" = "no" ] && [ "$Runsoverssh" = "no" ] && [ "$Hostmitshm" = "yes" ] && {
echo " warning 'Memory access failures and rendering glitches
may occure due to unrestricted cookie.
Avoid them with isolation breaking option --hostipc,
or use another X server option like --nxagent or --xpra.'"
}
echo " [ '$Trusted' = 'untrusted' ] && warning 'SECURITY RISK! Keylogging and remote host control "
echo " may be possible! Better avoid using option --hostdisplay,"
echo " rather use --nxagent or --xpra.'"
echo " cp $Hostxauthority $Xclientcookie"
}
echo "}"
echo "[ -s '$Xclientcookie' ] || { "
echo " # still no cookie? try to create one without extension security"
echo " debugnote 'xinitrc: Failed to retrieve trusted cookie from X server. Will bake one myself.'"
echo " echo 'Failed to retrieve trusted cookie from X server. Will bake one myself.'"
echo " xauth -v -i -f $Xclientcookie add :$Newdisplaynumber . $(makecookie)"
echo " ls -l $Xclientcookie"
echo "}"
echo ""
echo "# Prepare cookie with localhost identification disabled by ffff, needed if X socket is shared. ffff means 'familiy wild'"
echo "Cookie=\"\$(xauth -i -f $Xclientcookie nlist | sed -e 's/^..../ffff/')\""
echo "echo \"\$Cookie\" | xauth -v -i -f $Xclientcookie nmerge -"
echo ""
echo "debugnote \"xinitrc: Created cookie: \$(xauth -f $Xclientcookie list 2>&1)\""
echo "ls -l $Xclientcookie"
echo "cp $Xclientcookie $Xservercookie"
echo "chmod 644 $Xclientcookie"
echo ""
echo "[ -s '$Xclientcookie' ] || warning 'Cookie creation failed!'"
[ "$Xserver" = "--hostdisplay" ] && echo "[ '\$Xhostentry' = 'yes' ] && env XAUTHORITY=$Hostxauthority xhost -SI:localuser:$Hostuser"
[ "$Xserver" = "--nxagent" ] && echo "rm $Xclientcookie.bak"
}
echo "export XAUTHORITY=$Xclientcookie"
echo "[ '$Xauthentication' = 'no' ] || [ ! -s '$Xclientcookie' ] && unset XAUTHORITY && warning '$Xserver: X server $Newdisplay runs without cookie authentication.'"
echo ""
case "$Xserver" in
--hostdisplay) ;; # do not change host settings
--xwin) ;; # xhost does not work over tcp (?)
*)
case $Xauthentication in
yes)
echo "# clean xhost"
echo "verbose 'Disabling any possible access to new X server possibly granted by xhost'"
echo "disable_xhost"
;;
esac
[ -n "$Xhost" ] && {
echo "warning \"Option --xhost: Running 'xhost $Xhost' on $Newdisplay\""
echo "xhost $Xhost"
}
echo ""
;;
esac
case $Xserver in
--hostdisplay|--xwin|--nxagent) ;;
--hostwayland|--weston|--kwin|--tty) ;;
*)
echo "# Keyboard layout"
command -v setxkbmap >/dev/null && {
case "$Xkblayout" in
"") [ -n "$Hostdisplay" ] && setxkbmap -display $Hostdisplay -print >> $Xkbkeymapfile ;;
*) setxkbmap "$Xkblayout" -print >> $Xkbkeymapfile ;;
esac
:
} || {
note "setxkbmap not found. Need setxkbmap and xkbcomp to set keyboard layout.
$Wikipackages"
}
[ -s "$Xkbkeymapfile" ] && {
echo "# set keyboard layout on $Newdisplay"
echo "verbose \"Keyboard layout:"
echo "\$(cat $Xkbkeymapfile)\""
echo "xkbcomp $Xkbkeymapfile $Newdisplay"
}
echo ""
;;
esac
;;
esac
case $Xserver in
--xpra|--xpra-xwayland)
! verlt "$Xpraversion" "v2.3" && verlt "$Xprarelease" "r19606" && {
warning "Your xpra version has a cookie authentication issue.
Installed version is: xpra $Xpraversion
Recommended: Downgrade to xpra v2.2.5, upgrade to at least r19606,
or use another X server option.
Fallback: Setting 'xhost +SI:localuser:$Containeruser'"
check_fallback
echo "xhost +SI:localuser:$Containeruser"
echo ""
} ;;
esac
case $Xserver in
--xpra|--xvfb)
{ [ "$Xserver" = "--xpra" ] && [ "$Xpravfb" = "Xvfb" ] && [ "$Desktopmode" = "yes" ] ; } || [ "$Xserver" = "--xvfb" ] && {
echo "# create set of different screen resolutions"
echo "xrandr --newmode $Modeline"
echo "xrandr --addmode \$Output $(echo $Modeline | cut -d " " -f1)"
echo "while read Line; do"
echo " Line=\"\$(echo \"\$Line\" | sed 's/Modeline//g')\""
echo " Line=\"\$(echo \"\$Line\" | sed 's/\"//g')\""
echo " xrandr --newmode \$Line 2>/dev/null"
echo " xrandr --addmode \"\$Output\" \$(echo \$Line | cut -d' ' -f1) 2>/dev/null"
echo "done < \"$Modelinefile\""
}
echo ""
[ "$Xserver" = "--xpra" ] && [ "$Desktopmode" = "yes" ] && echo "xrandr --output \$Output --mode $(echo $Modeline | cut -d " " -f1)"
echo ""
;;
--xorg) # --xorg: --scale, --size, --rotate
echo "# determine screen size"
echo "xrandr | grep connected | grep -v disconnected && {"
[ -z "$Screensize" ] && {
echo " getscreensize"
echo " Xaxis=\"\$CurrentXaxis\""
echo " Yaxis=\"\$CurrentYaxis\""
[ "$Scaling" ] && echo " Xaxis=\"\$(awk -v a=\"\$Xaxis\" -v b=\"$Scaling\" 'BEGIN {print (a / b)}')\""
echo " Xaxis=\"\${Xaxis%.*}\""
[ "$Scaling" ] && echo " Yaxis=\"\$(awk -v a=\"\$Yaxis\" -v b=\"$Scaling\" 'BEGIN {print (a / b)}')\""
echo " Yaxis=\"\${Yaxis%.*}\""
} || {
echo " Xaxis='$Xaxis'"
echo " Yaxis='$Yaxis'"
}
echo " Screensize=\"\${Xaxis}x\${Yaxis}\""
echo ""
[ "$Screensize" ] && [ -z "$Scaling" ] && {
echo " # Switch to desired screen size $Screensize"
echo " [ -n \"\$(xrandr | grep \$Screensize)\" ] && { "
echo " note \"Will try to set native resolution \$Screensize."
echo " If that looks ugly, use --scale=1 to enforce a fake scaled resolution.\""
echo " xrandr --output \$Output --mode \$Screensize"
echo " } || note \"Resolution \$Screensize not found in xrandr.\""
echo ""
}
[ "$Screensize" ] && [ -z "$Scaling" ] && {
echo " checkscreensize || {"
echo " note \"Panning \$Screensize. If virtual screen is greater than "
echo " maximal screen size, you can move virtual screen with mouse at screen edges."
echo " You can force the virtual screen to match your monitor with option --scale=1\""
echo " xrandr --output \$Output --panning \$Screensize+0+0/\$Screensize+0+0/100/100/100/100 --verbose"
echo ' }'
echo " checkscreensize || {"
echo " note 'Panning failed, trying to scale instead.'"
echo " xrandr --output \$Output --scale-from \$Screensize --panning \$Screensize+0+0/\$Screensize+0+0"
echo " checkscreensize && note \"Successfully set screen size \$Screensize\""
echo ' }'
echo " checkscreensize || {"
echo " getscreensize"
echo " note \"Setting desired resolution \$Screensize failed."
echo " Fallback: Will use detected \${CurrentXaxis}x\${CurrentYaxis} instead.\""
echo ' }'
echo ""
}
[ "$Scaling" ] && {
echo " # --scale $Scaling"
[ "$Screensize" ] && [ "$Scaling" != "1" ] && echo " note 'Cannot set panning and scaling at the same time.
Desired screen size $Screensize will be scaled to your monitor size
for arbitrary values you may provide with option --scale.'"
echo " # Scaling $Scaling"
echo " note \"Setting scaled resolution \$Screensize\" with scale factor $Scaling."
# must use --scale-from and --panning because --scale causes mouse barriers/crtc-boundaries
echo " xrandr --output \$Output --scale-from \$Screensize --panning \$Screensize+0+0/\$Screensize+0+0 --verbose"
echo " checkscreensize || {"
echo " getscreensize"
echo " note \"Setting desired resolution \$Screensize failed."
echo " Detected resolution \${CurrentXaxis}x\${CurrentYaxis} instead.\""
echo " }"
echo ""
}
[ -n "$Rotation" ] && {
echo " # --rotate $Rotation"
echo " verbose 'Rotation $Rotation'"
case $Rotation in
0|normal) Rotation="" ;;
90) Rotation="--rotate right";;
180) Rotation="--reflect xy" ;;
270) Rotation="--rotate left";;
flipped) Rotation="--reflect y";;
flipped-90) Rotation="--rotate right --reflect x";;
flipped-180) Rotation="--reflect x";;
flipped-270) Rotation="--rotate left --reflect x";;
esac
echo " bash -c 'while read Line ; do xrandr --output \$Line $Rotation ; done < <(xrandr | grep \" connected\" | cut -d \" \" -f1)'"
echo ""
}
echo "} || {"
echo ' [ -z "$Xaxis" ] && Xaxis=1024 && Yaxis=768'
echo " Screensize=\"\${Xaxis}x\${Yaxis}\""
echo " note \"Could not detect any connected monitor."
echo " Running on a server? Will try to set a framebuffer size"
echo " with \"xrandr --fb \$Screensize\" that may serve as a virtual display.\""
echo " xrandr --fb \$Screensize"
echo "}"
echo ""
;;
esac
[ -n "$Newdisplay" ] && echo "verbose \"Output of xrandr on $Newdisplay
\$(xrandr)\""
echo ""
[ "$Xfishtank" = "yes" ] && echo "xfishtank & storepid \$! xfishtank"
[ "$Runfromhost" ] && {
echo "# custom host command added with option --runfromhost"
echo "$Runfromhost"
echo ""
}
echo "echo 'xinitrc: xinitrc is ready'"
echo "storeinfo xinitrc=ready"
echo ""
[ "$Shareclipboard" = "yes" ] && [ -n "$Hostdisplay" ] && {
case $Xserver in
--xpra|--xpra-xwayland|--nxagent|--xwin) ;; # have their own clipboard management
--hostdisplay) ;; # already same clipboard
*) # synchronizing between different X servers
echo "# option '-c, --clipboard': Run clipboard script "
echo "# (text copy only) (xpra has its own clipboard managment including images)"
echo "bash $Clipboardrc"
echo ""
;;
esac
}
echo "# wait for the end"
case $Usemkfifo in
yes) echo "read Var <$Timetosaygoodbyefifo" ;;
no) echo "while rocknroll; do sleep 1; done" ;;
esac
return 0
} >> $Xinitrc
#### docker command setup
check_containerhome() { # options --home, --homedir, --homebasedir: check HOME of container user.
## option '--home': Share folder ~/.local/share/x11docker/imagename with created container as its home directory
## option '--home=DIR': Share custom host folder as home
## option '--homebasedir': Specify base folder here to store container home folders for --home
# base home folder
[ "$Hosthomebasefolder" ] && { # --homebasedir
Hosthomebasefolder="$(convertpath subsystem "$Hosthomebasefolder")"
[ -e "$Hosthomebasefolder" ] || {
warning "Option --homebasedir: Specified path does not exist:
$Hosthomebasefolder
Fallback: Using default home base directory."
check_fallback
Hosthomebasefolder=""
}
}
[ "$Hosthomebasefolder" ] || case $Mobyvm in
no) Hosthomebasefolder="$Containeruserhosthome/.local/share/x11docker" ;;
yes) Hosthomebasefolder="$(convertpath subsystem "$(wincmd 'echo %userprofile%') ")/x11docker/home" ;;
esac
case $Sharehome in
yes|host)
[ -z "$Persistanthomevolume" ] && Persistanthomevolume="$Hosthomebasefolder/$Imagebasename"
Persistanthomevolume="$(sed "s%~%$Hostuserhome%" <<< "$Persistanthomevolume")"
[ "${Persistanthomevolume:0:1}" = "/" ] && Sharehome="host" || Sharehome="volume"
;;
esac
case $Sharehome in
host)
case $Createcontaineruser in
no)
note "Option --home or --home=DIR is not supported
with option --user=RETAIN.
Alternatively, specify a docker volume with --home=VOLUME.
Also you can use option --share to share host directories.
Fallback: Disabling option --home."
check_fallback
Sharehome="no"
;;
yes)
grep -q "unknown" <<< "$Containeruser" && {
note "Option --home: Sharing a host folder is allowed only
for container users that also exist on host.
You can use a docker volume with --home=VOLUME instead.
Fallback: Disabling option --home."
check_fallback
Sharehome="no"
}
;;
esac
;;
esac
case $Sharehome in
host)
Containeruserhomebasefolder="/home"
# A change can break existing configs, e.g. playonlinux
# Containeruserhomebasefolder="/home.x11docker"
[ "$Persistanthomevolume" = "$Containeruserhosthome" ] && {
# --home=$HOME must be same as on host #243
Containeruserhomebasefolder="$(dirname "$Containeruserhosthome")"
Containeruserhome="$Containeruserhosthome"
}
;;
no)
# Containeruserhomebasefolder="/home.tmp"
Containeruserhomebasefolder="/home"
;;
volume)
Containeruserhomebasefolder="/home.volume/$Persistanthomevolume"
grep -q "/" <<< "$Persistanthomevolume" && error "Option --home: Invalid argument: '$Persistanthomevolume'
Please either specify an absolute path beginning with '/'
or specify a docker volume without any '/'."
;;
esac
Containeruserhome="${Containeruserhome:-$Containeruserhomebasefolder/$Containeruser}"
case "$Createcontaineruser" in
no) store_runoption env "HOME=/tmp" ;;
esac
case $Sharehome in
host)
# if no home folder on host is specified (--home=DIR), create a standard one in ~/.local/share/x11docker
[ -d "$Persistanthomevolume" ] || {
[ "$Startuser" = "root" ] && su $Containeruser -c "mkdir -p '$Persistanthomevolume'"
[ "$Containeruser" = "$Hostuser" ] && unpriv "mkdir -p '$Persistanthomevolume'" && {
# create symbolic link to ~/x11docker
echo "$Persistanthomevolume" | grep -q .local/share/x11docker && [ ! -e "$Hostuserhome/x11docker" ] && unpriv "ln -s '$Hosthomebasefolder' '$Hostuserhome/x11docker'" ||:
}
}
[ -d "$Persistanthomevolume" ] || error "Option --home: Could not create persistent home folder for
user '$Containeruser' on host. Can e.g. happen with option --user.
Four possibilities to solve issue:
1.) Run x11docker one time as user '$Containeruser'.
2.) Run x11docker one time as user 'root'.
3.) Use option --home=DIR with DIR pointing to a writeable folder.
4.) Use option --home=VOLUME to use a docker volume."
writeaccess $Containeruseruid "$Persistanthomevolume" || warning "User '$Containeruser' might have no write access to
$Persistanthomevolume."
verbose "Sharing directory $Persistanthomevolume
with container as its home directory $Containeruserhome"
;;
volume)
debugnote "Option --home: Using docker volume $Persistanthomevolume"
;;
esac
return 0
}
check_containeruser() { # check container user and shared home folder (also option --user)
## check container user
[ "$Containeruser" = "RETAIN" ] && return 0
[ -z "$Containeruser" ] && Containeruser="$Hostuser" # default: containeruser = hostuser. can be changed with --user
[ -n "$Containeruser" ] && echo "$Containeruser" | grep -q ':' && { # option --user can specify a group/gid after :
Containerusergid="$(echo "$Containeruser" | cut -d: -f2)"
Containeruser="$(echo "$Containeruser" | cut -d: -f1)"
}
[ "$Containeruser" = "root" ] && Containeruser="0"
[ -n "$(getent passwd "$Containeruser")" ] && { # user exists on host
Containeruser=$(getent passwd "$Containeruser" | cut -d: -f1) # can be name or uid -> now name
Containeruseruid=$(getent passwd "$Containeruser" | cut -d: -f3)
[ -z "$Containerusergid" ] && Containerusergid="$(getent passwd $Containeruser | cut -d: -f4)"
[ "$Containeruser" = "$Hostuser" ] && Containeruserhosthome="$Hostuserhome"
[ -z "$Containeruserhosthome" ] && Containeruserhosthome="$(getent passwd "$Containeruser" | cut -d: -f6)"
:
} || { # user does not exist on host
[[ "$Containeruser" =~ ^[0-9]+$ ]] || error "Option --user: Unknown host user or invalid user number '$Containeruser'.
Non-host users can be specified with an UID only, not with a name."
Containeruseruid="$Containeruser"
Containeruser="unknown$Containeruseruid"
[ -z "$Containerusergid" ] && Containerusergid=100
Containeruserhosthome=""
}
Containerusergroup="$(getent group $Containerusergid | cut -d: -f1 || echo group_$Containeruser)"
[ "$Containeruseruid" = "0" ] && {
Containeruser="root"
Containerusergid="0"
Containerusergroup="root"
Containeruserhosthome="/root"
Sudouser="yes" && note "Option --user=root: Enabling option --sudouser."
}
store_runoption env "USER=$Containeruser"
debugnote "container user: $Containeruser $Containeruseruid:$Containerusergid $Containeruserhosthome"
case $X11dockermode in
run)
case $Containersetup in
no) store_runoption env "XDG_RUNTIME_DIR=/tmp" ;;
esac
;;
exe) store_runoption env "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" ;;
esac
return 0
}
create_dockercommand() { ### create command to run docker
local Line= Memory Tini=
Dockercommand="$Dockerexe run --tty"
Wmdockercommand="$Dockerexe run --detach"
#[ "$Preservecachefiles" = "no" ] && Dockercommand="$Dockercommand --rm"
case $Interactive in
yes) Dockercommand="$Dockercommand --interactive" ;;
no) Dockercommand="$Dockercommand --detach" ;;
esac
[ -z "$Containername" ] && Containername="x11docker_X${Newdisplaynumber}_${Codename}_${Cachenumber}"
Dockercommand="$Dockercommand \\
--name $Containername"
Wmdockercommand="$Wmdockercommand \\
--name ${Containername}_WM"
storeinfo "containername=$Containername"
[ "$Limitresources" ] && {
Memory="$(awk "BEGIN {print int($(free -b | awk 'NR==2 {print $7}') * $Limitresources)}")"
Dockercommand="$Dockercommand \\
--cpus=$(awk "BEGIN {print $(nproc) * $Limitresources}") \\
--memory=$Memory \\
--kernel-memory=$Memory"
}
# container user. init systems switch later.
case $Initsystem in
none|tini|dockerinit)
case $Switchcontaineruser in
no)
[ "$Createcontaineruser" = "yes" ] && Dockercommand="$Dockercommand \\
--user $Containeruseruid:$Containerusergid" ;;
yes)
Dockercommand="$Dockercommand \\
--user root" ;;
esac
;;
systemd|runit|openrc|sysvinit|s6-overlay)
Dockercommand="$Dockercommand \\
--user root" ;;
esac
Wmdockercommand="$Wmdockercommand \\
--user 1999:1999"
[ "$Createcontaineruser" = "yes" ] && {
# Disable user namespacing to avoid file permission issues with --home or --share. Files need same UID/GID.
case $Podman in
yes) Dockercommand="$Dockercommand \\
--userns=keep-id" ;;
no) $Dockerexe run --help | grep -q -- '--userns' && Dockercommand="$Dockercommand \\
--userns host" ;;
esac
}
# add container user groups, mainly video and audio and --group-add
[ "$Switchcontaineruser" = "no" ] && {
$Dockerexe run --help | grep -q -- '--group-add' && {
for Line in $Containerusergroups; do ### FIXME: should compare GIDs from host and container
getent group ${Line:-nonsense} >/dev/null && Dockercommand="$Dockercommand \\
--group-add $(getent group $Line | cut -d: -f3)"
done
:
} || {
note "Your docker version does not support option --group-add.
Could not add container user to groups: $Containerusergroups
Possible sound or GPU setup may fail. Please update your docker installation."
}
}
# Runtime runc|nvidia|kata
[ "$Runtime" ] && {
Dockercommand="$Dockercommand \\
--runtime='$Runtime'"
}
# option --hostipc
[ "$Sharehostipc" = "yes" ] && Dockercommand="$Dockercommand \\
--ipc host"
# option --network
[ "$Network" ] && Dockercommand="$Dockercommand \\
--network $Network"
# capabilities
[ "$Capdropall" = "yes" ] && Dockercommand="$Dockercommand \\
--cap-drop ALL"
while read Line ; do
Dockercommand="$Dockercommand \\
--cap-add $Line"
done < <(store_runoption dump cap)
# default no, do not gain privileges
[ "$Allownewprivileges" = "no" ] && Dockercommand="$Dockercommand \\
--security-opt no-new-privileges"
# SELinux restrictions for containers must be disabled to allow access to X socket. Flags z or Z do not help.
Dockercommand="$Dockercommand \\
--security-opt label=type:container_runtime_t"
Wmdockercommand="$Wmdockercommand \\
--cap-drop=ALL \
--security-opt=no-new-privileges \
--security-opt label=type:container_runtime_t"
# stop signal for some init systems
[ "$Stopsignal" ] && Dockercommand="$Dockercommand \\
--stop-signal $Stopsignal"
# setup and shared files for some init systems
case $Initsystem in
dockerinit)
Dockercommand="$Dockercommand \\
--init"
;;
tini)
Tini="$Tinicontainerpath --"
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$Tinibinaryfile:ro" "$Tinicontainerpath")"
;;
systemd)
Dockercommand="$Dockercommand \\
-v $Systemdtarget:/etc/systemd/system/x11docker.target:ro \\
-v $Systemdconsoleservice:/lib/systemd/system/console-getty.service:ro \\
-v $Systemdwatchservice:/etc/systemd/system/x11docker-watch.service:ro \\
-v $Systemdjournalservice:/etc/systemd/system/x11docker-journal.service \\
-v $Systemdenvironment:/etc/systemd/system.conf.d/x11docker.environment.conf"
;;
esac
# option --sharecgroup
[ "$Sharecgroup" = "yes" ] && Dockercommand="$Dockercommand \\
--volume /sys/fs/cgroup:/sys/fs/cgroup:ro"
# Needed especially for --init=systemd and --dbus-daemon
$Dockerexe run --help | grep -q -- '--tmpfs' && Dockercommand="$Dockercommand \\
--tmpfs /run --tmpfs /run/lock" || {
note "Your docker version does not support option --tmpfs.
Options like --init=systemd may fail.
Please update your docker installation."
}
# shared cache folder
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$Sharefolder:rw" $Sharefoldercontainer)"
# --home
case $Sharehome in
host)
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$Persistanthomevolume:rw" "$Containeruserhome")"
;;
volume)
Dockercommand="$Dockercommand \\
--volume '$Persistanthomevolume':'$Containeruserhomebasefolder':rw"
;;
esac
# --share
while read -r Line; do
case "$Line" in
"$Containeruserhosthome")
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$Containeruserhosthome" "/home.host/$Containeruser")"
;;
*)
case "$(cut -c1-5 <<< "$Line")" in
"/dev/")
Dockercommand="$Dockercommand \\
--device $(convertpath volume "$Line")"
warning "Sharing device file: $Line"
;;
*)
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$Line")"
;;
esac
;;
esac
done < <(store_runoption dump volume)
[ "$Xauthentication" = "yes" ] && Wmdockercommand="$Wmdockercommand \\
--volume $(convertpath volume "$Xclientcookie" "$(convertpath share $Xclientcookie)")"
# --gpu: share NVIDIDA driver installer
[ -e "$Nvidiainstallerfile" ] && Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$Nvidiainstallerfile:ro" "$Nvidiacontainerfile")"
# X socket will be softlinked to /tmp/.X11-unix in containerrc
[ "$Newxsocket" ] && {
case $Containersetup in
yes)
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$Newxsocket" "/X$Newdisplaynumber")"
;;
no)
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$Newxsocket")"
;;
esac
Wmdockercommand="$Wmdockercommand \\
--volume $(convertpath volume "$Newxsocket")"
}
# Wayland socket will be softlinked to XDG_RUNTIME_DIR in containerrc
[ "$Setupwayland" = "yes" ] && {
case $Containersetup in
yes)
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$XDG_RUNTIME_DIR/$Newwaylandsocket" "/$Newwaylandsocket")"
;;
no)
Dockercommand="$Dockercommand \\
--volume $(convertpath volume "$XDG_RUNTIME_DIR/$Newwaylandsocket")"
;;
esac
}
## options --pulseaudio, --alsa
{ [ "$Pulseaudiomode" = "socket" ] || [ "$Sharealsa" = "yes" ] ; } && Dockercommand="$Dockercommand \\
--volume $Pulseaudioconf:/etc/pulse/client.conf:ro"
## option --workdir or /tmp
case $Containersetup in
yes)
Dockercommand="$Dockercommand \\
--workdir '${Workdir:-/tmp}'"
;;
no)
[ "$Workdir" ] && Dockercommand="$Dockercommand \\
--workdir '$Workdir'"
;;
esac
case $Containersetup in
yes)
# real entrypoint is checked in dockerrc
Dockercommand="$Dockercommand \\
--entrypoint env"
;;
esac
# add environment variables. Only needed here for possible 'docker exec'. Otherwise set in containerrc
while read Line; do
Dockercommand="$Dockercommand \\
--env '$Line'"
grep -q "DISPLAY" <<< "$Line" && Wmdockercommand="$Wmdockercommand \\
--env '$Line'"
grep -q "XAUTHORITY" <<< "$Line" && Wmdockercommand="$Wmdockercommand \\
--env '$Line'"
done < <(store_runoption dump env)
# add custom docker arguments, imagename and imagecommand
[ "$Customdockeroptions" ] && Dockercommand="$Dockercommand \\
$Customdockeroptions"
Dockercommand="$Dockercommand \\
--"
case $Containersetup in
yes)
case $Switchcontaineruser in
no) Dockercommand="$Dockercommand $Imagename $Tini /bin/sh - $(convertpath share $Containerrc)" ;; # dockerrc runs containerrootrc with 'docker exec'
yes) Dockercommand="$Dockercommand $Imagename $Tini /bin/sh - $(convertpath share $Containerrootrc)" ;; # containerrootrc runs containerrc
esac
;;
no)
Dockercommand="$Dockercommand $Imagename $Containercommand"
;;
esac
return 0
}
setup_capabilities() { # check linux capabilities needed by container
# compare: man capabilities
[ "$Sudouser" = "yes" ] && Adminusercaps="yes"
[ "$Capdropall" = "no" ] && [ "$Allownewprivileges" = "auto" ] && {
note "Option --cap-default: Enabling option --newprivileges=yes.
You can avoid this with --newprivileges=no"
Allownewprivileges="yes"
}
# --sudouser
[ "$Sudouser" = "yes" ] && warning "Option --sudouser severly reduces container security.
Container gains additional capabilities to allow sudo and su.
If an application breaks out of container, it can harm your system
in many ways without you noticing. Password: x11docker"
# enable dbus
case $Initsystem in
systemd|sysvinit|openrc|runit) Dbussystem="yes" ;;
esac
[ "$Dbussystem" = "yes" ] && {
Dbusrunsession="yes"
store_runoption cap "CHOWN FOWNER" ### FIXME: CHOWN needed indeed here?
Switchcontaineruser="yes"
}
case $Initsystem in
none|tini|dockerinit) ;;
systemd)
Switchcontaineruser="yes"
Sharecgroup="yes"
store_runoption cap "FSETID FOWNER SETPCAP SYS_BOOT"
;;
runit|openrc|sysvinit)
Switchcontaineruser="yes"
store_runoption cap "SYS_BOOT KILL"
;;
s6-overlay)
Switchcontaineruser="yes"
store_runoption cap "CHOWN KILL"
;;
esac
[ "$Sharecgroup" = "yes" ] && Switchcontaineruser="yes" # needed for elogind
[ "$Switchcontaineruser" = "yes" ] && Switchcontainerusercaps="yes"
[ "$Adminusercaps" = "yes" ] && {
Switchcontainerusercaps="yes"
store_runoption cap "CHOWN KILL FSETID FOWNER SETPCAP"
[ "$Allownewprivileges" = "auto" ] && {
note "Option --sudouser: Enabling option --newprivileges=yes.
You can avoid this with --newprivileges=no"
Allownewprivileges="yes"
}
}
[ "$Switchcontainerusercaps" = "yes" ] && store_runoption cap "SETUID SETGID DAC_OVERRIDE AUDIT_WRITE"
# Automated NVIDIA driver installation
[ "$Sharegpu" = "yes" ] && [ "$Nvidiainstallerfile" ] && [ "$Switchcontaineruser" = "yes" ] && store_runoption cap "CHOWN FOWNER"
[ "$Allownewprivileges" = "auto" ] && Allownewprivileges="no"
[ "$Allownewprivileges" = "yes" ] && warning "Option --newprivileges=yes: x11docker does not set
docker run option --security-opt=no-new-privileges.
That degrades container security.
However, this is still within a default docker setup."
# Issues with hidepid=2 seen on NixOS (issue #83)
{ [ "$Switchcontaineruser" = "yes" ] || [ "$Containeruser" != "$Hostuser" ] ; } && {
[ "$Hostcanwatchroot" = "no" ] && {
[ "$Hosthidepid" = "yes" ] && Message="/proc is mounted with hidepid=2." || Message="Cannot watch processes of other users for unknown reasons."
Message="$Message
x11docker cannot watch processes of root
or other users different from $Hostuser."
[ "$Hostuser" != "$Containeruser" ] && Message="$Message
Container user $Containeruser is different from host user $Hostuser."
[ "$Switchcontaineruser" = "yes" ] && Message="$Message
Container PID 1 will run as root."
Message="$Message
Therefore x11docker cannot watch container processes
for a clean termination of X and x11docker itself.
Four possible solutions:
1. Run x11docker as root.
2. Don't use options like --user or --init=systemd that change container user.
3. Add user $Hostuser to group 'proc'.
4. Change /proc mount option hidepid=2 to hidepid=1."
error "$Message"
}
}
return 0
}
setup_initsystem() { # option init: set up capabilities, check or create files
# some init system setup also in containerrootrc
local Message=
case $Initsystem in
tini|systemd|sysvinit|openrc|runit|dockerinit|s6-overlay|none) ;;
*)
note "Option --init: Unknown init system $Initsystem
Possible: tini systemd sysvinit openrc runit s6-overlay none
Fallback: Using --init=tini instead."
check_fallback
Initsystem="tini"
;;
esac
# --init in Mobyvm. /usr/bin/docker-init is not available in MSYS2/Cygwin/WSL1
case $Mobyvm in
yes) [ "$Initsystem" = "tini" ] && Initsystem="dockerinit" ;;
esac
case "$X11dockermode" in
exe)
case $Initsystem in
tini|none) ;;
dockerinit) Initsystem="none" ;;
*)
note "Option --init: Only --init[=tini] or --init=none are
supported with option --exe. Fallback: Setting option --init=tini"
check_fallback
Initsystem="tini"
;;
esac
;;
run)
store_runoption env "container=docker" # At least OpenRC and systemd regard this hint
;;
esac
case $Initsystem in
none|dockerinit) ;;
tini)
Tinibinaryfile="$(command -v docker-init ||:)"
[ -z "$Tinibinaryfile" ] && Tinibinaryfile="/snap/docker/current/bin/docker-init"
[ -e "$Tinibinaryfile" ] || Tinibinaryfile="/snap/docker/current/usr/bin/docker-init"
[ -e "/usr/local/share/x11docker/tini-static" ] && Tinibinaryfile="/usr/local/share/x11docker/tini-static"
[ -e "$Hostuserhome/.local/share/x11docker/tini-static" ] && Tinibinaryfile="$Hostuserhome/.local/share/x11docker/tini-static"
Tinibinaryfile="$(myrealpath "$Tinibinaryfile" 2>/dev/null ||:)"
[ -e "$Tinibinaryfile" ] || Tinibinaryfile=""
[ "$Tinibinaryfile" ] && {
case "$Runtime" in
kata-runtime)
# avoid sharing same file that might be shared with runc already.
mkdir -p "$Hostuserhome/.local/share/x11docker"
cp -u "$Tinibinaryfile" "$Hostuserhome/.local/share/x11docker/tini-static-kata"
Tinibinaryfile="$Hostuserhome/.local/share/x11docker/tini-static-kata"
;;
esac
[ -x "$Tinibinaryfile" ] || {
chmod +x "$Tinibinaryfile" || {
warning "Your tini binary is not executeable. Please run
chmod +x $Tinibinaryfile"
Initsystem="none"
}
}
} || Initsystem="none"
[ "$Initsystem" = "none" ] && [ "$X11dockermode" = "run" ] && {
note "Did not find container init system 'tini'.
This is a bug in your distributions docker package.
Normally, docker provides init system tini as '/usr/bin/docker-init'.
x11docker uses tini for clean process handling and fast container shutdown.
To provide tini yourself, please download tini-static:
https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
Store it in one of:
$Hostuserhome/.local/share/x11docker/
/usr/local/share/x11docker/"
}
verbose "--init: Found tini binary: ${Tinibinaryfile:-(none)}"
[ "$Tinibinaryfile" ] && storeinfo "tini=$Tinibinaryfile"
;;
systemd)
Stopsignal="SIGRTMIN+3"
Containerusergroups="$Containerusergroups systemd-journal"
echo "[Unit]
Description=x11docker target
Wants=multi-user.target
After=multi-user.target
[Install]
Also=console-getty.service
Also=x11docker-watch.service
Also=x11docker-journal.service
" >> $Systemdtarget
echo "[Unit]
Description=x11docker start service
# start on console to support --interactive
# runs x11docker-agetty->x11docker-login->containerrc
Wants=multi-user.target
Wants=x11docker-watch.service
Wants=x11docker-journal.service
Wants=dbus.service
After=systemd-user-sessions.service
After=rc-local.service getty-pre.target
Before=getty.target
[Service]
ExecStart=/usr/local/bin/x11docker-agetty
StandardInput=tty
StandardOutput=tty
Type=idle
UtmpIdentifier=cons
TTYPath=/dev/console
TTYReset=yes
TTYVHangup=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes
[Install]
WantedBy=x11docker.target
WantedBy=getty.target
WantedBy=multi-user.target
" >> $Systemdconsoleservice
echo "[Unit]
Description=x11docker watch service
# Watches for end of containerrc and initiates shutdown
[Service]
Type=simple
ExecStart=/bin/sh -c 'while sleep 1; do systemctl is-active console-getty >/dev/null || { echo timetosaygoodbye >>$(convertpath share $Timetosaygoodbyefile) ; systemctl halt ; } ; [ -s $(convertpath share $Timetosaygoodbyefile) ] && systemctl halt ; done'
[Install]
WantedBy=x11docker.target
" >> $Systemdwatchservice
echo "[Unit]
Description=x11docker journal log service
# get systemd log to transfer it into x11docker.log
[Service]
Type=simple
ExecStart=/bin/sh -c '/bin/journalctl --follow --no-tail --merge >> $(convertpath share $Systemdjournallogfile) 2>&1'
[Install]
WantedBy=x11docker.target
" >> $Systemdjournalservice
echo "[Manager]
DefaultEnvironment=$(while read -r Line; do echo -n "$Line " ; done < <(store_runoption dump env))
" >> $Systemdenvironment
;;
runit)
Stopsignal="HUP"
store_runoption env "VIRTUALIZATION=docker"
;;
openrc)
;;
sysvinit)
Stopsignal="INT"
;;
s6-overlay)
;;
esac
case $Initsystem in
systemd)
warning "Option --init=systemd slightly degrades container isolation.
It adds some user switching capabilities x11docker would drop otherwise.
However, they are still within default docker capabilities.
Not within default docker capabilities it adds capability SYS_BOOT.
It shares access to host cgroups in /sys/fs/cgroup.
Some processes in container will run as root."
;;
runit|openrc|sysvinit)
warning "Option --init=$Initsystem slightly degrades container isolation.
It adds some user switching capabilities x11docker would drop otherwise.
However, they are still within default docker capabilities.
Not within default docker capabilities it adds capability SYS_BOOT.
Some processes in container will run as root."
;;
s6-overlay)
warning "Option --init=$Initsystem slightly degrades container isolation.
It adds some user switching capabilities x11docker would drop otherwise.
However, they are still within default docker capabilities.
Some processes in container will run as root."
;;
tini|none|dockerinit)
[ "$Dbussystem" = "yes" ] && {
[ "$Capdropall" = "yes" ] && warning "Option --dbus=system slightly degrades container isolation.
It adds some user switching capabilities x11docker would drop otherwise.
However, they are still within default docker capabilities.
Some processes in container will run as root.
--dbus=system might need further capabilities or --cap-default to work
as expected. If in doubt, one of --init=systemd|openrc|runit|sysvinit
might be a better choice."
note "Option --dbus=system with init system '$Initsystem'
can have a quite long timeout delay until startup.
Use one of --init=systemd|openrc|runit|sysvinit in that case."
}
;;
esac
return 0
}
store_runoption() { # store env, cap or volume/device for docker command
# $1 env store environment variable $2
# volume store volume or device path $2
# cap store apability $2
# dump dump all entries of $2
local Count Line Path Readwritemode
case ${1:-} in
env)
Containerenvironmentcount="$((Containerenvironmentcount + 1))"
Containerenvironment[$Containerenvironmentcount]="${2:-}"
;;
volume)
Path="$(convertpath subsystem "${2:-}")"
Readwritemode="$(echo "${2:-}" | rev | cut -c1-3 | rev)"
[ "$Readwritemode" = ":ro" ] || Readwritemode=""
case "${Path:0:1}" in
"/")
[ -e "$Path" ] && {
Sharevolumescount="$((Sharevolumescount + 1))"
Sharevolumes[$Sharevolumescount]="${2:-}"
[ -h "$Path" ] && myrealpath "$Path" >/dev/null && {
note "Option --share: Shared file is a symbolic link. Sharing target, too.
Symlink: $Path
Target: $(myrealpath "$Path")"
store_runoption volume "$(myrealpath "$Path")$Readwritemode"
}
}
[ -e "$Path" ] || note "Option --share: Path not found:
$Path"
;;
*)
grep -q '/' <<< "$Path" && error "Option --share: Invalid argument $Path.
Either specify an absolute path beginning with '/'
or specify a docker volume without any '/'."
Sharevolumescount="$((Sharevolumescount + 1))"
Sharevolumes[$Sharevolumescount]="${2:-}"
;;
esac
;;
cap)
for Line in ${2:-} ; do
Capabilities="$Capabilities
$Line"
done
;;
dump)
case ${2:-} in
env) for ((Count=$Containerenvironmentcount ; Count>=1 ; Count --)) ; do echo "${Containerenvironment[$Count]}" ; done ;;
volume) for ((Count=1 ; Count<=$Sharevolumescount ; Count ++)) ; do echo "${Sharevolumes[$Count]}" ; done ;;
cap)
while read Line; do
[ "$Line" ] && case $Capdropall in
yes) echo "$Line" ;;
no) grep -w -q "$Line" <<< "SETPCAP MKNOD AUDIT_WRITE CHOWN NET_RAW DAC_OVERRIDE FOWNER FSETID KILL SETGID SETUID NET_BIND_SERVICE SYS_CHROOT SETFCAP" || echo "$Line" ;;
esac
done < <(echo "$Capabilities" | sort -u)
;;
esac
;;
esac
return 0
}
#### docker helper scripts
create_containerrootrc() { ### create containerrootrc: This script runs as root in container
local Line=
echo "#! /bin/sh"
echo ""
echo "# containerrootrc"
echo "# This Script is executed as root in container."
echo "# - Create container user"
echo "# - Time zone"
echo "# - Install NVIDIA driver if requested"
echo "# - Set up init system services and DBus for --init=systemd|openrc|runit|sysvinit"
echo ""
# echo "set -x"
echo "# redirect output to have it available before 'docker logs' starts. --init=runit (void) would eat up the output at all for unknown reasons."
echo "exec 1>>$(convertpath share $Containerlogfile) 2>&1"
echo ""
declare -f storeinfo
declare -f rocknroll
echo "$Messagefifofuncs"
echo "Messagefile=$(convertpath share $Messagefifo)"
echo "Storeinfofile='$(convertpath share $Storeinfofile)'"
echo "Timetosaygoodbyefile=$(convertpath share $Timetosaygoodbyefile)"
echo ""
echo "debugnote 'Running containerrootrc: Setup as root in container'"
echo ""
echo "Error=''"
echo "for Line in cat chmod chown cut cd cp date echo env export grep id ln ls mkdir mv printf rm sed sh sleep tail touch; do"
echo " command -v \"\$Line\" || {"
echo " warning \"ERROR: Command not found in image: \$Line\""
echo " Error=1"
echo " }"
echo "done"
echo "[ \"\$Error\" ] && error 'Commands for container setup missing in image.
You can try with option --no-setup to avoid this error.'"
echo ""
echo "# Check type of libc"
echo "ldd --version 2>&1 | grep -q 'musl libc' && Containerlibc='musl'"
echo "ldd --version 2>&1 | grep -q -E 'GLIBC|GNU libc' && Containerlibc='glibc'"
echo 'debugnote "containerrootrc: Container libc: $Containerlibc"'
echo ""
echo "# Prepare X environment"
echo "# Create some system dirs with needed permissions"
echo "mkdir -v -p /var/lib/dbus /var/run/dbus"
echo "mkdir -v -p -m 1777 /tmp/.ICE-unix /tmp/.X11-unix /tmp/.font-unix"
echo "chmod -c 1777 /tmp/.ICE-unix /tmp/.X11-unix /tmp/.font-unix"
echo "export DISPLAY=$Newdisplay XAUTHORITY=$(convertpath share $Xclientcookie)"
[ "$Xoverip" = "no" ] && {
echo "[ -e /X$Newdisplaynumber ] && ln -s /X$Newdisplaynumber $Newxsocket" # done again in containerrc. At least x11docker/deepin needs it here already.
echo "ls -l /X$Newdisplaynumber"
echo "ls -l $Newxsocket"
}
echo ""
[ "$Screensize" ] && {
echo "# workaround: autostart of xrandr for some desktops like deepin, cinnamon and gnome to fix wrong autoresize"
echo "echo '#! /bin/sh
Output=\$(xrandr | grep ' connected' | cut -d\" \" -f1)
Mode=$Screensize
xrandr --output \$Output --mode \$Mode\n\
' > /usr/local/bin/x11docker-xrandr"
echo "chmod +x /usr/local/bin/x11docker-xrandr"
echo "mkdir -p /etc/xdg/autostart"
echo "echo '[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=x11docker-xrandr
Comment=
Exec=/usr/local/bin/x11docker-xrandr
' > /etc/xdg/autostart/x11docker-xrandr.desktop"
}
echo ""
echo "# Time zone"
[ "$Hostlocaltimefile" ] && {
echo '[ ! -d /usr/share/zoneinfo ] && [ "$Containerlibc" = "'$Hostlibc'" ] && {'
echo " mkdir -p $(dirname $Hostlocaltimefile)"
echo " cp '$(convertpath share $Containerlocaltimefile)' '$Hostlocaltimefile'"
echo "}"
echo "[ -e '$Hostlocaltimefile' ] && ln -f -s '$Hostlocaltimefile' /etc/localtime"
echo ""
}
echo "# Container system"
echo "Containersystem=\"\$(grep '^ID=' /etc/os-release 2>/dev/null | cut -d= -f2 || echo 'unknown')\""
echo "verbose \"Container system ID: \$Containersystem\""
echo ""
echo "# Environment variables"
while read -r Line; do
echo "export '$Line'"
done < <(store_runoption dump env)
echo ""
echo "# Check container user"
echo "Containeruser=\"\$(storeinfo dump containeruser)\"" # reading root access
echo ""
case $Createcontaineruser in
yes)
echo "Containeruserhome='$Containeruserhome'"
# create container user
echo "# Create user entry in /etc/passwd (and delete possibly existing same uid)"
echo "cat /etc/passwd | grep -v ':$Containeruseruid:' > /tmp/passwd" ### FIXME gids same as uid would be deleted, too
echo ""
echo "# Disable possible /etc/shadow passwords for other users"
echo "sed -i 's%:x:%:-:%' /tmp/passwd"
case $Containerusershell in
auto) echo "bash --version >/dev/null 2>&1 && Containerusershell=/bin/bash || Containerusershell=/bin/sh" ;;
*) echo "Containerusershell='$Containerusershell'" ;;
esac
echo "Containeruserentry=\"$Containeruser:x:$Containeruseruid:$Containerusergid:$Containeruser,,,:$Containeruserhome:\$Containerusershell\""
echo 'debugnote "containerrootrc: $Containeruserentry"'
echo 'echo "$Containeruserentry" >> /tmp/passwd'
echo ""
echo "rm /etc/passwd"
echo "mv /tmp/passwd /etc/passwd || warning 'Unable to change /etc/passwd. That may be a security risk.'"
echo ""
echo "# Create password entry for container user in /etc/shadow"
echo "rm -v /etc/shadow || warning 'Cannot change /etc/shadow. That may be a security risk.'"
echo "echo \"$Containeruser:$Containeruserpassword:17293:0:99999:7:::\" > /etc/shadow"
case $Sudouser in
no) echo "echo 'root:*:17219:0:99999:7:::' >> /etc/shadow" ;;
yes) echo "echo 'root:$Containeruserpassword:17219:0:99999:7:::' >> /etc/shadow # with option --sudouser, set root password 'x11docker'"
echo "sed -i 's%root:-:%root:x:%' /etc/passwd # allow password in /etc/shadow"
;;
esac
echo ""
echo "# Create user group entry (and delete possibly existing same gid)"
echo "cat /etc/group | grep -v ':$Containerusergid:' > /tmp/group"
echo "echo \"$Containerusergroup:x:$Containerusergid:\" >> /tmp/group"
echo "mv /tmp/group /etc/group"
echo ""
# sudo configuration
echo "# Create /etc/sudoers, delete /etc/sudoers.d. Overwrite possible sudo setups in image."
echo "[ -e /etc/sudoers.d ] && rm -v -R /etc/sudoers.d"
echo "[ -e /etc/sudoers ] && rm -v /etc/sudoers"
echo "echo '# /etc/sudoers created by x11docker' > /etc/sudoers"
echo "echo 'Defaults env_reset' >> /etc/sudoers"
echo "echo 'root ALL=(ALL) ALL' >> /etc/sudoers"
[ "$Sudouser" = "yes" ] && echo "echo '$Containeruser ALL=(ALL) ALL' >> /etc/sudoers"
echo ""
# try to disable possible custom PAM setups that could allow switch to root in container
[ "$Sudouser" = "no" ] && {
echo "# Restrict PAM configuration of su and sudo"
echo "mkdir -p /etc/pam.d"
echo "[ -e /etc/pam.d/sudo ] && rm -v /etc/pam.d/sudo"
echo 'case "$Containersystem" in'
echo ' fedora)'
echo " echo '#%PAM-1.0' > /etc/pam.d/su"
echo " echo 'auth sufficient pam_rootok.so' >> /etc/pam.d/su"
#echo " echo 'auth substack system-auth' >> /etc/pam.d/su"
#echo " echo 'auth include postlogin' >> /etc/pam.d/su"
echo " echo 'account sufficient pam_succeed_if.so uid = 0 use_uid quiet' >> /etc/pam.d/su"
#echo " echo 'account include system-auth' >> /etc/pam.d/su"
#echo " echo 'password include system-auth' >> /etc/pam.d/su"
echo " echo 'session include system-auth' >> /etc/pam.d/su"
#echo " echo 'session include postlogin' >> /etc/pam.d/su"
#echo " echo 'session optional pam_xauth.so' >> /etc/pam.d/su"
echo ' ;;'
echo ' *)'
echo " echo '#%PAM-1.0' > /etc/pam.d/su"
echo " echo 'auth sufficient pam_rootok.so' >> /etc/pam.d/su # allow root to switch user without a password"
echo " echo '@include common-auth' >> /etc/pam.d/su"
echo " echo '@include common-account' >> /etc/pam.d/su"
echo " echo '@include common-session' >> /etc/pam.d/su"
echo ' ;;'
echo 'esac'
echo ""
}
;;
no)
# check container user home. Can miss with --user=RETAIN
echo "Containeruserhome=\"\$(cat /etc/passwd | grep '\$Containeruser:.:' | cut -d: -f6)\""
echo "Containeruserhome=\"\${Containeruserhome:-/tmp/\$Containeruser}\""
echo ""
echo "debugnote \"containerrootrc: Container user: \$(id \$Containeruser)
\$(cat /etc/passwd | grep '\$Containeruser:.:')\""
echo ""
;;
esac
# /etc/group
echo "# Set up container user groups"
for Line in $Containerusergroups ; do
echo "Groupname=\"$(cat /etc/group 2>/dev/null | grep "$Line" | cut -d: -f1)\""
echo "Groupid=\"$(cat /etc/group 2>/dev/null | grep "$Line" | cut -d: -f3)\""
echo "[ \"\$Groupname\" ] || Groupname=\"\$(cat /etc/group | grep \"$Line\" | cut -d: -f1)\""
echo "[ \"\$Groupid\" ] || Groupid=\"\$(cat /etc/group | grep \"$Line\" | cut -d: -f3)\""
echo "[ \"\$Groupname\" ] && {"
echo " cat /etc/group | sed \"s/^\$Groupname.*/\$Groupname:x:\$Groupid:\$(cat /etc/group | grep \"\$Groupname:.:\" | cut -d: -f4 ),\$Containeruser/\" | sed 's/:,/:/' > /tmp/group"
echo " cat /etc/group | grep -q \"\$Groupname:.:\" || echo \"\$Groupname:x:\$Groupid:\$Containeruser\" >> /tmp/group"
echo " cp /tmp/group /etc/group"
echo "} || note 'Failed to add container user to group $Line.'"
echo ""
done
# HOME
echo "# Create HOME"
echo "mkdir -p \$Containeruserhome"
echo "chown \$Containeruser:\$(id -g \$Containeruser) \"\$Containeruserhome\""
echo "ls -la \$Containeruserhome"
echo ""
# --gpu with closed source nvidia driver
[ "$Nvidiainstallerfile" ] && {
echo "# Install NVIDIA driver"
echo "Nvidiaversion=\"\$(nvidia-settings -v 2>/dev/null | grep version | rev | cut -d' ' -f1 | rev)\""
echo '[ "$Nvidiaversion" ] && note "Found NVIDIA driver $Nvidiaversion in image."'
echo 'case "$Nvidiaversion" in'
echo " $Nvidiaversion) note 'NVIDIA driver version in image matches version on host. Skipping installation.' ;;"
echo " *)"
echo " Installationwillsucceed=maybe"
echo ' case "$Containerlibc" in'
echo " musl) note 'Installing NVIDIA driver in container systems
based on musl libc like Alpine is not possible due to
proprietary closed source policy of NVIDIA corporation.'"
echo " Installationwillsucceed=no"
echo " ;;"
echo " esac"
echo " case \$Containersystem in"
echo " opensuse)"
echo " note \"Nvidia driver installation probably fails in \$Containersystem.
You can try to install nvidia driver $Nvidiaversion in image yourself.\""
echo " ;;"
echo " esac"
echo " [ \"\$Installationwillsucceed\" = \"maybe\" ] && {"
echo " note 'Installing NVIDIA driver $Nvidiaversion in container.'"
echo " mkdir -m 1777 /tmp2"
echo " # provide fake tools to fool installer dependency check"
echo " ln -s /bin/true /tmp2/modprobe"
echo " ln -s /bin/true /tmp2/depmod"
echo " ln -s /bin/true /tmp2/lsmod"
echo " ln -s /bin/true /tmp2/rmmod"
echo " ln -s /bin/true /tmp2/ld"
echo " ln -s /bin/true /tmp2/objcopy"
echo " ln -s /bin/true /tmp2/insmod"
echo " Nvidiaoptions='--accept-license --no-runlevel-check --no-questions --no-backup --ui=none --no-kernel-module --no-nouveau-check'"
echo " env TMPDIR=/tmp2 PATH=\"/tmp2:\$PATH\" sh $Nvidiacontainerfile -A | grep -q -- '--install-libglvnd' && Nvidiaoptions=\"\$Nvidiaoptions --install-libglvnd\""
echo " env TMPDIR=/tmp2 PATH=\"/tmp2:\$PATH\" sh $Nvidiacontainerfile -A | grep -q -- '--no-nvidia-modprobe' && Nvidiaoptions=\"\$Nvidiaoptions --no-nvidia-modprobe\""
echo " env TMPDIR=/tmp2 PATH=\"/tmp2:\$PATH\" sh $Nvidiacontainerfile -A | grep -q -- '--no-kernel-module-source' && Nvidiaoptions=\"\$Nvidiaoptions --no-kernel-module-source\""
echo " env TMPDIR=/tmp2 PATH=\"/tmp2:\$PATH\" sh $Nvidiacontainerfile --tmpdir /tmp2 \$Nvidiaoptions || note 'ERROR: Installation of NVIDIA driver failed.
Run with option --verbose to see installer output.'"
echo " rm -R /tmp2 && unset TMPDIR"
echo " } || note 'Skipping installation of $Nvidiacontainerfile'"
echo " ;;"
echo "esac"
echo ""
}
echo "rocknroll || exit 64"
echo ""
[ "$Switchcontaineruser" = "yes" ] && {
echo "# Create some helper scripts"
echo "mkdir -p /usr/local/bin"
echo ""
echo "echo \"#! /bin/sh
# Send messages to x11docker on host.
# To be sourced by other scripts.
$Messagefifofuncs_escaped
Messagefile=$(convertpath share $Messagefifo)
\" >/usr/local/bin/x11docker-message"
echo ""
echo "echo \"#! /bin/sh
# User switch from root in containerrootrc to unprivileged user in containerrc.
# Additionally, su triggers logind and elogind. (Except su from busybox?)
# Called by x11docker-agetty.
. /usr/local/bin/x11docker-message
debugnote 'Running x11docker-login'
chmod +x $(convertpath share $Containerrc)
exec su - -s /bin/sh \$Containeruser $(convertpath share $Containerrc)
\" >/usr/local/bin/x11docker-login"
echo "chmod +x /usr/local/bin/x11docker-login"
echo ""
echo "echo \"#! /bin/sh
# Run agetty to get a valid console.
# Needed at least for --interactive.
# Runs x11docker-login.
# Called at different places depending on init system.
. /usr/local/bin/x11docker-message
debugnote 'Running x11docker-agetty'
[ -e /sbin/agetty ] && exec agetty -a \$Containeruser -l /usr/local/bin/x11docker-login console
debugnote 'x11docker-agetty: agetty not found.'
[ '$Interactive' = 'yes' ] && note '/sbin/agetty not found. --interactive can fail.
Please install package util-linux in image.'
exec /usr/local/bin/x11docker-login
\" >/usr/local/bin/x11docker-agetty"
echo "chmod +x /usr/local/bin/x11docker-agetty"
echo ""
echo "echo \"#! /bin/sh
# Wait for end of x11docker and shut down container.
# Started in background by x11docker for sysvinit|runit|openrc.
. /usr/local/bin/x11docker-message
debugnote 'Running x11docker-watch'
read Dummy <$(convertpath share $Timetosaygoodbyefifo)
echo timetosaygoodbye >>$(convertpath share $Timetosaygoodbyefifo)
debugnote 'x11docker-watch: $Initsystem shutdown now'
shutdown 0
systemctl poweroff
openrc-shutdown --poweroff 0
halt
halt -f
\" >/usr/local/bin/x11docker-watch"
echo "chmod +x /usr/local/bin/x11docker-watch"
echo ""
}
case $Initsystem in
tini|none|dockerinit) ;;
systemd)
echo "# --init=systemd"
echo "# enable x11docker CMD service"
echo "systemctl unmask console-getty.service"
echo "systemctl enable console-getty.service"
echo "systemctl enable x11docker-journal.service"
echo ""
echo "systemctl unmask systemd-logind"
echo "systemctl enable systemd-logind"
echo ""
echo "# remove failing and annoying services"
echo "Unservicelist='
apt-daily.service
apt-daily.timer
apt-daily-upgrade.service
apt-daily-upgrade.timer
bluetooth.service
cgproxy.service
deepin-anything-monitor.service
deepin-sync-daemon.service
display-manager.service
fprintd.service
gdm3.service
gvfs-udisks2-volume-monitor.service
hwclock_stop.service
lastore-daemon.service
lastore-update-metadata-info.service
lightdm.service
NetworkManager.service
plymouth-quit.service
plymouth-quit-wait.service
plymouth-read-write.service
plymouth-start.service
rtkit-daemon.service
sddm.service
systemd-localed.service
systemd-hostnamed.service
tracker-extract.service
tracker-miner-fs.service
tracker-store.service
tracker-writeback.service
udisks2.service
upower.service
'"
echo "for Service in \$(find /lib/systemd/system/* /usr/lib/systemd/user/* /etc/systemd/system/* /etc/systemd/user/*) ; do"
echo ' echo "$Unservicelist" | grep -q "$(basename $Service)" && {'
echo ' debugnote "--init=systemd: Removing $Service"'
echo ' rm $Service'
echo ' }'
echo "done"
echo "# Fix for Gnome 3"
echo 'sed -i "s/ProtectHostname=yes/ProtectHostname=no/" /lib/systemd/system/systemd-logind.service'
;;
runit)
echo "# --init=runit"
echo "# create and enable x11docker service containing container command"
echo "mkdir -p /etc/sv/x11docker"
echo "mkdir -p /etc/runit/runsvdir/default"
echo "mkdir -p /etc/runit/1.d"
echo "mkdir -p /service"
echo ""
echo "echo \"#! /bin/sh
$(declare -f mysleep)
waitforservice() {
Service=\\\$1
[ \\\"\\\$(sv check \\\$Service | cut -d: -f1)\\\" = 'ok' ] && {
echo \"x11docker: waiting for service \\\$Service ...\"
for Count in $(seq -s' ' 20); do
[ \\\"\\\$(sv status \\\$Service | cut -d: -f1)\\\" = 'down' ] && mysleep 0.2 || break
done
}
}
# make stderr visible
exec 2>&1
# wait for all other services
echo 'Content of /etc/runit/runsvdir/default:'
ls -la /etc/runit/runsvdir/default/*
for Service in /etc/runit/runsvdir/default/* ; do waitforservice \\\$Service ;done
echo 'Current status of runit services:'
for Service in /etc/runit/runsvdir/default/* ; do sv status \\\$Service ;done
/usr/local/bin/x11docker-agetty
\" > /etc/sv/x11docker/run"
echo "chmod +x /etc/sv/x11docker/run"
echo ""
echo "echo \"#! /bin/sh
sv down x11docker
runit-init 0
init 0
shutdown -h 0
halt
\" > /etc/sv/x11docker/finish"
echo "chmod +x /etc/sv/x11docker/finish"
echo ""
echo "ln -s /etc/sv/x11docker /etc/runit/runsvdir/default" #void
echo "ln -s /etc/sv/x11docker /service" #alpine
echo ""
echo "[ -e /etc/runit/1 ] || echo '#!/usr/bin/env sh
set -eu
chmod 100 /etc/runit/stopit
/bin/run-parts --exit-on-error /etc/runit/1.d || exit 100
' >/etc/runit/1"
echo "chmod +x /etc/runit/1"
echo ""
echo "[ -e /etc/runit/2 ] || echo '#!/usr/bin/env sh
set -eu
runsvdir -P /service \"log: ..................................................................\"
' >/etc/runit/2"
echo "chmod +x /etc/runit/2"
echo ""
echo '[ -e /etc/runit/3 ] || echo "#!/usr/bin/env sh
set -eu
exec 2>&1
echo \"Waiting for services to stop...\"
sv -w196 force-stop /service/*
sv exit /service/*
# kill any other processes still running in the container
for ORPHAN_PID in $(ps --no-headers -eo \"%p,\" -o stat | tr -d \" \" | grep \"Z\" | cut -d, -f1); do
timeout 5 /bin/sh -c \"kill \$ORPHAN_PID && wait \$ORPHAN_PID || kill -9 \$ORPHAN_PID\"
done
" >/etc/runit/3'
echo "chmod +x /etc/runit/3"
echo ""
echo "touch /etc/runit/stopit"
;;
openrc)
echo "# --init=openrc"
echo "# Create and enable x11docker service containing container command"
echo "printf \"#!/sbin/openrc-run
name=x11docker
depend() {
after *
}
start() {
ebegin 'Starting containerrc'
/usr/local/bin/x11docker-agetty
openrc-shutdown --poweroff 0
shutdown 0
halt
halt -f
eend \$?
}
\" > /etc/init.d/x11docker.service"
echo ""
echo "chmod +x /etc/init.d/x11docker.service"
echo "rc-update add x11docker.service default"
echo ""
echo "# Tell openrc that it runs in docker container"
echo "sed -e 's/#rc_sys=\"\"/rc_sys=\"docker\"/g' -i /etc/rc.conf"
;;
sysvinit)
echo "# --init=sysvinit"
echo "# Adding x11docker start command to rc.local"
echo "sed -i '/exit 0/d' /etc/rc.local"
echo "echo \"/usr/local/bin/x11docker-agetty || echo \\\"x11docker: Exit code of x11docker-agetty: \\\$?\\\"
echo 'x11docker: rc.local sends shutdown -h now'
shutdown -h now
exit 0\" >> /etc/rc.local"
echo "chmod +x /etc/rc.local"
;;
esac
echo ""
echo "# disable getty in inittab"
echo "[ -e /etc/inittab ] && sed -i 's/.*getty/##getty disabled by x11docker## \0/' /etc/inittab"
echo ""
case $Dbussystem in
yes)
echo "# Set up DBus"
echo "command -v dbus-daemon && {"
echo ' Unservicelist="
org.bluez
org.bluez.obex
org.freedesktop.hostname1
org.freedesktop.network1
org.freedesktop.resolve1
org.freedesktop.secrets
org.freedesktop.systemd1
org.freedesktop.timedate1
org.freedesktop.Tracker1
org.freedesktop.Tracker1.Miner.Extract
org.freedesktop.UDisks2
org.freedesktop.UPower
org.gtk.vfs.UDisks2VolumeMonitor
org.opensuse.CupsPkHelper.Mechanism
com.deepin.daemon.Bluetooth
com.deepin.daemon.Grub2
com.deepin.daemon.Power
com.deepin.lastore
com.deepin.lastore.Smartmirror
com.deepin.sync.Daemon
com.deepin.sync.Helper
com.deepin.userexperience.Daemon
"'
echo ' for Service in /usr/share/dbus-1/system-services/* /usr/share/dbus-1/services/*; do' # find is not available on fedora
echo ' Name="$(cat $Service | grep Name= | cut -d= -f2)"'
echo ' Command="$(cat $Service | grep Exec= | cut -d= -f2)"'
echo ' echo "$Unservicelist" | grep -q -w "$Name" && {'
echo ' debugnote "DBus: Removing $Name: $Service"'
echo ' rm $Service'
echo ' }'
# echo ' [ -e "$Service" ] && [ "$Command" != "/bin/false" ] && debugnote "DBus: Found $Name: $Command"'
echo ' case $Name in'
[ "$Initsystem" != "systemd" ] && {
echo ' org.freedesktop.systemd1|org.freedesktop.hostname1|org.freedesktop.locale1)'
echo ' debugnote "DBus: Removing $Name: $Service"'
echo ' rm "$Service"'
echo ' ;;'
}
echo ' org.freedesktop.login1)'
echo ' debugnote "DBus: Found login service $Name: $Command"'
[ "$Sharecgroup" = "no" ] && {
echo ' debugnote "DBus: $Name: Removing $Service"'
echo ' rm "$Service"'
echo ' echo "$Command" | grep -q elogind && {'
echo ' note "Found login service elogind.'
echo ' If you want to use it, enable option --sharecgroup."'
echo ' }'
}
echo ' ;;'
echo ' esac'
echo ' done'
echo ''
case $Initsystem in
systemd)
echo " # Just assuming that a DBus service file will be present"
echo " systemctl unmask dbus"
echo " systemctl enable dbus"
;;
sysvinit)
echo " echo '#!/bin/sh
### BEGIN INIT INFO
# Provides: dbus
# Required-Start: \$remote_fs \$syslog
# Required-Stop: \$remote_fs \$syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: D-Bus systemwide message bus
# Description: D-Bus is a simple interprocess messaging system, used
# for sending messages between applications.
### END INIT INFO
# -*- coding: utf-8 -*-
# Debian init.d script for D-BUS
# Copyright © 2003 Colin Walters <walters@debian.org>
# Copyright © 2005 Sjoerd Simons <sjoerd@debian.org>
#
DAEMON=/usr/bin/dbus-daemon
UUIDGEN=/usr/bin/dbus-uuidgen
UUIDGEN_OPTS=--ensure
NAME=dbus
DAEMONUSER=messagebus
PIDDIR=/var/run/dbus
PIDFILE=\"\$PIDDIR/pid\"
DESC=\"system message bus\"
#
test -x \$DAEMON || exit 1
. /lib/lsb/init-functions
# Source defaults file; edit that file to configure this script.
PARAMS=""
if [ -e /etc/default/dbus ]; then
. /etc/default/dbus
fi
create_machineid() {
# Create machine-id file
if [ -x \$UUIDGEN ]; then
\$UUIDGEN \$UUIDGEN_OPTS
fi
}
start_it_up() {
[ -d \$PIDDIR ] || {
mkdir -p \$PIDDIR
chown \$DAEMONUSER \$PIDDIR
chgrp \$DAEMONUSER \$PIDDIR
}
mountpoint -q /proc/ || {
log_failure_msg \"Cannot start \$DESC - /proc is not mounted\"
return 1
}
[ -e \$PIDFILE ] && {
\$0 status > /dev/null && {
log_success_msg \"\$DESC already started; not starting.\"
return 0
}
log_success_msg \"Removing stale PID file \$PIDFILE.\"
rm -f \$PIDFILE
}
create_machineid
log_daemon_msg \"Starting \$DESC\" \"\$NAME\"
start-stop-daemon --start --quiet --pidfile \$PIDFILE --exec \$DAEMON -- --system \$PARAMS
log_end_msg \$?
}
shut_it_down() {
log_daemon_msg \"Stopping \$DESC\" \"\$NAME\"
start-stop-daemon --stop --retry 5 --quiet --oknodo --pidfile \$PIDFILE --user \$DAEMONUSER
log_end_msg \$?
rm -f \$PIDFILE
}
reload_it() {
create_machineid
log_action_begin_msg \"Reloading \$DESC config\"
dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig > /dev/null
log_action_end_msg \$?
}
case \$1 in
start) start_it_up ;;
stop) shut_it_down ;;
reload|force-reload) reload_it ;;
restart)
shut_it_down
start_it_up
;;
status) status_of_proc -p \$PIDFILE \$DAEMON \$NAME && exit 0 || exit \$? ;;
esac' > /etc/init.d/dbus"
echo " chmod +x /etc/init.d/dbus"
;;
runit)
echo " [ -e /etc/sv/dbus ] || {"
echo " mkdir -p /etc/sv/dbus"
echo " echo '#!/bin/sh
[ ! -d /run/dbus ] && install -m755 -g 22 -o 22 -d /run/dbus
exec dbus-daemon --system --nofork --nopidfile' >/etc/sv/dbus/run"
echo " echo '#!/bin/sh
exec dbus-send --system / org.freedesktop.DBus.Peer.Ping > /dev/null 2> /dev/null' >/etc/sv/dbus/check"
echo " chmod +x /etc/sv/dbus/run /etc/sv/dbus/check"
echo " }"
echo " verbose 'DBus: enabling dbus service'"
echo " ln -s /etc/sv/dbus /etc/runit/runsvdir/default" # void
echo " ln -s /etc/sv/dbus /service" # alpine
;;
openrc)
echo " echo '#!/sbin/openrc-run
start() {
ebegin \"Starting D-BUS system messagebus\"
/usr/bin/dbus-uuidgen --ensure=/etc/machine-id
mkdir -p /var/run/dbus
start-stop-daemon --start --pidfile /var/run/dbus.pid --exec /usr/bin/dbus-daemon -- --system
eend \$?
}
stop() {
ebegin \"Stopping D-BUS system messagebus\"
start-stop-daemon --stop --pidfile /var/run/dbus.pid
retval=\$?
eend \${retval}
[ -S /var/run/dbus/system_bus_socket ] && rm -f /var/run/dbus/system_bus_socket
return \${retval}
}
reload() {
ebegin \"Reloading D-BUS messagebus config\"
/usr/bin/dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig > /dev/null
retval=\$?
eend \${retval}
return \${retval}
}' >/etc/init.d/dbus && chmod +x /etc/init.d/dbus"
echo " verbose 'DBus: enabling dbus service'"
echo " rc-update add dbus default"
;;
esac
echo "} || note 'DBus not found.
Can not run DBus system daemon. Please install dbus in image.'"
;;
esac
echo ""
echo "rocknroll || exit 64"
echo ""
# --lang
while read Line; do
echo "# --lang: Language locale"
echo "verbose \"Searching for language locale matching $Line\""
echo "Locales=\"\$(locale -a)\""
echo "Langall=\"\$(cat /usr/share/i18n/SUPPORTED | grep -E 'UTF-8|utf8' | cut -d' ' -f1 | cut -d. -f1 | cut -d@ -f1 | sort | uniq)\""
echo "Langland=\"\$(echo $Line | cut -d. -f1)\""
echo "Langcontainer=''"
echo ""
echo "echo \"\$Langland\" | grep -q '_' || {"
echo " Langland=\"\$(echo \$Langland | tr '[:upper:]' '[:lower:]')_\$(echo \$Langland | tr '[:lower:]' '[:upper:]')\""
echo " echo \"\$Langall\" | grep -q \"\$Langland\" || {"
echo " echo \"\$Langall\" | grep -i -q \"$Line\" && {"
echo " Langland=\"\$(echo \"\$Langall\" | grep -i -m1 \"$Line\")\""
echo " }"
echo " }"
echo "}"
echo ""
echo "Langland=\"\$(echo \"\$Langland\" | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')_\$(echo \"\$Langland\" | cut -d_ -f2 | tr '[:lower:]' '[:upper:]')\""
echo ""
echo "echo \"\$Locales\" | grep -q \"\$Langland.UTF-8\" && Langcontainer=\"\$Langland.UTF-8\""
echo "echo \"\$Locales\" | grep -q \"\$Langland.utf8\" && Langcontainer=\"\$Langland.utf8\""
echo ""
echo "[ -z \"\$Langcontainer\" ] && {"
echo " [ -e /usr/share/i18n/SUPPORTED ] || note \"Option --lang: /usr/share/i18n/SUPPORTED not found.
Please install package 'locales' in image (belongs to glibc).
Look here to find a package for your image system:
https://github.com/mviereck/x11docker/wiki/dependencies#dependencies-in-image\""
echo ""
echo " Langcontainer=\"\$Langland.utf8\""
echo " note \"Option --lang: Generating language locale \$Langcontainer\"."
echo ""
echo " command -v localedef >/dev/null || note 'Option --lang: Command localedef not found in image.
Need it for language locale creation.
Look here to find a package for your image system:
https://github.com/mviereck/x11docker/wiki/dependencies#dependencies-in-image'"
echo " localedef --verbose --force -i \"\$Langland\" -f UTF-8 \$Langcontainer || verbose \"localedef exit code: \$?\""
echo ""
echo " locale -a | grep -q \"\$Langcontainer\" || {"
echo " note \"Option --lang: Generation of locale \$Langcontainer failed.\""
echo " Langcontainer=''"
echo " }"
echo "} || {"
echo " debugnote \"Option --lang: Found locale in image: \$Langcontainer\""
echo "}"
echo ""
echo "[ \"\$Langcontainer\" ] && {"
echo ' storeinfo locale="$Langcontainer"'
echo " echo \"LANG=\$Langcontainer\" > /etc/default/locale"
echo "} || note 'Option --lang: Desired locale for '$Line' not found and not generated.'"
echo ""
done < <(tac <<< "$Langwunsch" | grep . ||:)
[ "$Langwunsch" ] && {
echo "debugnote \"Option --lang: Output of locale -a:"
echo "\$(locale -a)\""
echo ""
echo "rocknroll || exit 64"
echo ""
}
[ "$Dbussystem" = "yes" ] && {
case $Initsystem in
tini|none|dockerinit) echo "dbus-daemon --system --fork" ;;
esac
}
# --runasroot command added here
[ "$Runasroot" ] && {
echo "# Custom setup root command added with option --runasroot"
echo "$Runasroot"
echo ""
}
echo "storeinfo containerrootrc=ready" # signal for containerrc
echo ""
[ "$Switchcontaineruser" = "yes" ] && { # if "no", containerrc is executed in command line $Dockercommand
echo "# --init=$Initsystem"
case $Initsystem in
none|dockerinit)
# echo "exec /usr/local/bin/x11docker-login"
echo "exec /usr/local/bin/x11docker-agetty"
;;
tini)
# echo "exec $Tinicontainerpath -- /usr/local/bin/x11docker-agetty"
echo "exec /usr/local/bin/x11docker-agetty"
;;
sysvinit)
echo "/usr/local/bin/x11docker-watch &"
echo "exec /sbin/init"
;;
openrc)
echo "/usr/local/bin/x11docker-watch &"
echo "command -v openrc-init && exec openrc-init || exec /sbin/init"
;;
runit)
echo "/usr/local/bin/x11docker-watch &"
echo "[ -e /sbin/runit-init ] && exec runit-init || exec /sbin/init"
;;
s6-overlay)
echo "exec /init /usr/local/bin/x11docker-agetty"
;;
systemd)
echo 'Systemd=/lib/systemd/systemd'
echo '[ -e "$Systemd" ] || Systemd=/bin/systemd'
echo '[ -e "$Systemd" ] || Systemd=/sbin/systemd'
echo '[ -e "$Systemd" ] || {'
echo ' command -v systemctl && {'
echo ' warning "Executeable for systemd not found. Will try /sbin/init"'
echo ' Systemd=/sbin/init'
echo ' } || error "systemd not found in image (option --init=systemd)."'
echo '}'
echo 'exec $Systemd'
;;
esac
}
return 0
} >> $Containerrootrc
create_dockerrc() { ### create dockerrc: This script runs as root (or member of group docker) on host. Also creates containerrc
# create containerrc -> runs as unprivileged user in container
# check and set up cgroup on host for systemd or elogind
# run docker
local Line= Wantcgroup= Path= Ungrep=
echo "#! /usr/bin/env bash"
echo ""
echo "# dockerrc:"
echo "# This script runs as root (or member of group docker) on host."
echo "# - inspect image"
echo "# - pull image if needed"
echo "# - create containerrc"
echo "# - set up systemd/elogind cgroup if needed"
echo "# - run window manager in container or from host if needed"
echo ""
echo "trap '' SIGINT"
echo ""
declare -f askyesno
declare -f checkpid
declare -f escapestring
declare -f mysleep
declare -f pspid
declare -f rmcr
declare -f rocknroll
declare -f saygoodbye
declare -f storeinfo
declare -f storepid
declare -f waitforlogentry
echo "$Messagefifofuncs"
echo ""
[ "$Winsubsystem" = "MSYS2" ] && {
echo "# avoid path conversion in MSYS2 commands"
echo "export MSYS2_ARG_CONV_EXCL='*'"
echo ""
}
echo "Containercommand=\"$Containercommand\""
echo "Imagename=\"$Imagename\""
echo "Messagefile='$Messagefifo'"
echo "Newxenv='$Newxenv'"
echo "export PATH='$PATH'"
echo "Storeinfofile='$Storeinfofile'"
echo "Storepidfile='$Storepidfile'"
echo "Timetosaygoodbyefile='$Timetosaygoodbyefile'"
echo "Timetosaygoodbyefifo='$Timetosaygoodbyefifo'"
echo "Xserver='$Xserver'"
echo "Workdir='$Workdir'"
echo ""
echo "Containerarchitecture="
echo "Containerid="
echo "Containerip="
echo "Dockerlogspid=''"
echo "Dockerpull="
echo "Exec="
echo "Entrypoint="
echo "Failure="
echo "Imageuser="
echo "Inspect="
echo "Line="
echo "Pid1pid="
echo "Runtime="
echo "Signal="
echo "Windowmanagermode="
echo "Windowmanagercommand="
echo "Wmcontainerid="
echo "Wmdockercommand="
echo "debugnote 'Running dockerrc: Setup as root or as user docker on host.'"
[ "$Debugmode" = "yes" ] && {
echo "PS4='+ dockerrc: \$(date +%S+%3N) '"
#echo "set -x"
#declare -f traperror | sed 's/Command/dockerrc: Command/'
echo "traperror() { # trap ERR: --debug: Output for 'set -o errtrace'"
echo ' debugnote "dockerrc: Command at Line ${2:-} returned with error code ${1:-}:
${4:-}
${3:-} - ${5:-}"'
echo " saygoodbye dockerrc-traperror"
echo " exit 64"
echo "}"
echo "set -Eu"
echo "trap 'traperror \$? \$LINENO \$BASH_LINENO \"\$BASH_COMMAND\" \$(printf \"::%s\" \${FUNCNAME[@]})' ERR"
}
echo ""
# transfer DOCKER_* environment variables, e.g. DOCKER_HOST.
# can get lost e.g. if using --pw=sudo or --pw=pkexec
while read Line; do
debugnote "dockerrc: Found docker environment variable: $Line"
echo "export '$Line'"
done < <(env | grep -e '^DOCKER_' ||:)
echo ""
echo "# Check whether docker daemon is running, get docker info"
echo "$Dockerexe info >>$Dockerinfofile 2>>$Containerlogfile || {
error \"Calling docker daemon failed.
Is docker daemon running at all?
Try to start docker daemon with: systemctl start docker
Last lines of log:
\$(rmcr < '$Containerlogfile' | tail)\"
}"
echo ""
echo "# Check default runtime"
echo "Runtime=\"\$( { grep 'Default Runtime' < '$Dockerinfofile' ||: ;} | awk '{print \$3}' )\""
echo "debugnote \"dockerrc: Found default Runtime: \$Runtime\""
echo "debugnote \"dockerrc: All \$(grep 'Runtimes' < '$Dockerinfofile' ||: )\""
echo "[ \"\$Runtime\" != '$Runtime' ] && {"
echo " case \$Runtime in"
echo " kata-runtime) warning 'Found default docker runtime kata-runtime.
Please run x11docker with --runtime=kata-runtime to avoid issues.' ;;"
echo " nvidia) [ '$Sharegpu' = 'yes' ] && warning 'Option --gpu: Found default docker runtime nvidia.
Please run x11docker with --runtime=nvidia to avoid issues.' ;;"
echo " runc|crun|oci) ;;"
echo " *) note \"Found unknown container runtime: \$Runtime
Please report at: https://github.com/mviereck/x11docker\" ;;"
echo " esac"
[ "$Runtime" ] && echo " Runtime='$Runtime'"
echo "}"
echo "debugnote \"dockerrc: Container Runtime: \$Runtime\""
echo "storeinfo \"runtime=\$Runtime\""
echo ""
echo "# Refresh images.list for x11docker-gui"
echo "$Dockerexe images 2>>$Containerlogfile | grep -v REPOSITORY | awk '{print \$1 \":\" \$2}' >>$Dockerimagelistfile.sort"
echo "rmcr $Dockerimagelistfile.sort"
echo "while read -r Line ; do"
echo ' grep -q "<none>" <<<$Line || echo $Line >> '$Dockerimagelistfile
echo "done < <(sort < $Dockerimagelistfile.sort)"
echo "rm $Dockerimagelistfile.sort"
echo ""
echo "# Check if image $Imagename is available locally"
echo "Dockerpull=no"
case $Pullimage in
no) ;;
always) echo "Dockerpull=yes" ;;
yes) echo "$Dockerexe inspect $Imagename >>$Containerlogfile 2>&1 || Dockerpull=yes" ;;
ask)
[ "$Runsinterminal" = "yes" ] && {
echo "grep -x -q '$Imagename' < $Dockerimagelistfile || grep -x -q '$Imagename:latest' < $Dockerimagelistfile || {"
echo " $Dockerexe inspect $Imagename >>$Containerlogfile 2>&1 || {"
echo " echo 'Image $Imagename not found locally.' >&2"
echo " echo 'Do you want to pull it from docker hub?' >&2"
echo " askyesno && Dockerpull=yes || error \"Image '$Imagename' not available locally and not pulled from docker hub.\""
echo " }"
echo "}"
}
;;
esac
echo ""
echo "rocknroll || exit 64"
echo ""
echo "[ \"\$Dockerpull\" = 'yes' ] && {"
echo " note \"Pulling image '$Imagename' from docker hub\""
[ "$Runsinterminal" = "no" ] && case $Passwordneeded in
no) echo " env DISPLAY='$Hostdisplay' DBUS_SESSION_BUS_ADDRESS='${DBUS_SESSION_BUS_ADDRESS:-}' bash -c \"notify-send 'x11docker: Pulling image $Imagename from docker hub'\" 2>/dev/null &" ;;
yes) echo " env DISPLAY='$Hostdisplay' DBUS_SESSION_BUS_ADDRESS='${DBUS_SESSION_BUS_ADDRESS:-}' su '$Hostuser' -c \"notify-send 'x11docker: Pulling image $Imagename from docker hub'\" 2>/dev/null &" ;;
esac
echo " $Sudo $Dockerexe pull $Imagename 1>&2 || error \"Pulling docker image '$Imagename' seems to have failed!\""
echo "}"
echo ""
echo "rocknroll || exit 64"
echo ""
echo "Inspect=\"\$($Dockerexe inspect $Imagename --format='{{.Config.Entrypoint}}{{.Config.Cmd}}[{{.Config.User}}][{{.Config.WorkingDir}}][{{.Architecture}}]')\""
echo ""
echo "# Check architecture"
echo "Containerarchitecture=\"\$(cut -d[ -f6 <<< \"\$Inspect\" | cut -d] -f1)\""
echo "debugnote \"dockerrc: Image architecture: \$Containerarchitecture\""
echo "# Check CMD"
echo "[ -z \"\$Containercommand\" ] && {"
echo " # extract image command from image if not given on cli"
echo " Containercommand=\"\$(cut -d] -f2 <<< \"\$Inspect\" | cut -d[ -f2)\""
echo " debugnote \"dockerrc: Image CMD: \$Containercommand\""
echo " echo \"\$Containercommand\" | grep -q $(convertpath share $Containerrc) && error 'Recursion error: Found CMD $(convertpath share $Containerrc) in image.
Did you use docker commit with an x11docker container?
Please build new images with a Dockerfile instead of using docker commit,
or provide a different container command.'"
echo "}"
echo ""
echo "# Check USER"
echo "Imageuser=\"\$(cut -d[ -f4 <<< \"\$Inspect\" | cut -d] -f1)\""
echo "debugnote \"dockerrc: Image USER: \$Imageuser\""
case $Createcontaineruser in
yes)
echo "[ \"\$Imageuser\" ] && note \"Found 'USER \$Imageuser' in image."
echo " If you want to run with user \$Imageuser instead of host user $Containeruser,"
echo " than run with --user=RETAIN.\""
echo "storeinfo containeruser=\"$Containeruser\""
;;
no)
echo 'storeinfo containeruser="${Imageuser:-root}"'
;;
esac
echo ""
case $Noentrypoint in
yes) echo "Entrypoint=" ;;
no)
echo "# Check ENTRYPOINT"
echo "Entrypoint=\"\$(cut -d] -f1 <<< \"\$Inspect\" | cut -d[ -f2)\""
echo "debugnote \"dockerrc: Image ENTRYPOINT: \$Entrypoint\""
case $Initsystem in
systemd|sysvinit|runit|openrc|tini)
echo "echo \"\$Entrypoint\" | grep -qE 'tini|init|systemd' && {"
echo " note \"There seems to be an init system in ENTRYPOINT of image:
\$Entrypoint
Will disable it as x11docker already runs an init with option --$Initsystem.
To allow this ENTRYPOINT, run x11docker with option --init=none.\""
echo " Entrypoint="
echo "}"
#echo "Exec=exec"
;;
s6-overlay)
echo "[ \"\$Entrypoint\" = '/init' ] && {"
echo " Entrypoint="
echo " [ \"\$Containercommand\" ] || Containercommand=\"sh -c 'while :; do sleep 10; done'\""
echo "}"
;;
none)
echo "echo \"\$Entrypoint\" | grep -qE 'tini|init|systemd' && {"
echo " note \"There seems to be an init system in ENTRYPOINT of image:
\$Entrypoint
Returning exit code of container command will fail.\""
echo " Exec=exec"
echo "}"
;;
esac
;;
esac
echo ""
[ -z "$Workdir" ] && {
echo "# Check WORKDIR"
echo "Workdir=\"\$(cut -d[ -f5 <<< \"\$Inspect\" | cut -d] -f1)\""
echo "debugnote \"dockerrc: Image WORKDIR: \$Workdir\""
echo "[ \"\$Workdir\" ] && note \"Found 'WORKDIR \$Workdir' in image.
You can change it with option --workdir=DIR.\""
echo ""
}
echo "[ -z \"\$Containercommand\$Entrypoint\" ] && error 'No container command specified and no CMD or ENTRYPOINT found in image.'"
echo ""
echo "######## Create $(basename $Containerrc) ########"
echo ""
echo "{ echo '#! /bin/sh'"
# [ "$Debugmode" = "yes" ] && echo "echo 'set -x'"
echo " echo ''"
echo " echo '# $(basename $Containerrc)'"
echo " echo '# Created startscript for docker run used as container command.'"
echo " echo '# Runs as unprivileged user in container.'"
echo " echo ''"
# echo " echo 'exec 6>&1 7>&2'"
# echo " echo 'exec >>$(convertpath share $Containerlogfile) 2>&1'"
echo " echo ''"
echo " echo '$(declare -f mysleep)'"
echo " echo '$(declare -f rocknroll)'"
echo " echo '$(declare -f saygoodbye)'"
echo " echo '$(declare -f storeinfo)'"
echo " echo '$(declare -f waitforlogentry)'"
echo " echo '$Messagefifofuncs'"
echo " echo 'Messagefile=$(convertpath share $Messagefifo)'"
echo " echo 'Storeinfofile=$(convertpath share $Storeinfofile)'"
echo " echo 'Timetosaygoodbyefile=$(convertpath share $Timetosaygoodbyefile)'"
echo " echo ''"
echo " echo 'waitforlogentry $(basename $Containerrc) \$Storeinfofile containerrootrc=ready "" infinity'"
echo " echo 'debugnote \"Running $(basename $Containerrc): Unprivileged user commands in container\"'"
echo " echo ''"
echo ' echo "Containercommand=\"$Containercommand\""'
echo ' echo "Entrypoint=\"$Entrypoint\""'
echo " echo ''"
echo " echo 'verbose \"$(basename $Containerrc): Container system:'"
echo " echo '\$(cat /etc/os-release 2>&1 ||:)\"'"
echo " echo ''"
echo "} >> $Containerrc"
[ "$Switchcontaineruser" = "yes" ] && { ### FIXME try --format '{{json .ContainerConfig.Env}}'
echo "echo '# Environment variables found in image:' >> $Containerrc"
echo "IFS=$'\n'"
echo "while read -r Line; do"
echo " echo \"export \$(escapestring \"\$Line\")\" >> $Containerrc"
echo "done < <($Dockerexe run --rm --entrypoint env $Imagename env 2>>$Containerlogfile | rmcr | grep -v 'HOSTNAME=' )"
echo "IFS=$' \t\n'"
}
echo "{"
echo " echo ''"
echo " echo '# USER and HOME'"
echo " echo 'Containeruser=\"\$(storeinfo dump containeruser)\"'"
case $Createcontaineruser in
yes)
echo " echo 'Containeruserhome=\"$Containeruserhome\"'"
;;
no)
case $Sharehome in
no)
echo " echo 'Containeruserhome=\"\$(cat /etc/passwd | grep \"\$Containeruser:.:\" | cut -d: -f6)\"'"
echo " echo 'Containeruserhome=\"\${Containeruserhome:-/tmp/\$Containeruser}\"'"
echo " echo 'mkdir -p \"\$Containeruserhome\"'"
;;
volume)
echo " echo 'Containeruserhome=\"$Containeruserhome\"'"
;;
esac
;;
esac
echo " echo 'export USER=\"\$Containeruser\"'"
echo " echo 'export HOME=\"\$Containeruserhome\"'"
echo " echo ''"
echo " echo '# XDG_RUNTIME_DIR'"
echo " echo 'Containeruseruid=\$(id -u \$Containeruser)'"
echo " echo 'export XDG_RUNTIME_DIR=/tmp/XDG_RUNTIME_DIR'"
echo " echo '[ -e /run/user/\$Containeruseruid ] && ln -s /run/user/\$Containeruseruid \$XDG_RUNTIME_DIR || mkdir -p -m700 \$XDG_RUNTIME_DIR'"
echo " echo ''"
while read -r Line; do
Path="$(convertpath container "$Line")"
[ "$(cut -c1-5 <<< "$Line")" != "/dev/" ] && {
case "$Line" in
"$Containeruserhosthome")
echo " echo '# --share: create soft link of shared $Containeruserhosthome to container home'"
echo " echo 'Homesoftlink=\"\$Containeruserhome/home.host.$Containeruser\"'"
echo " echo '[ -e \"\$Containeruserhome/home.host.$Containeruser\" ] || ln -s \"/home.host/\$Containeruser\" \"\$Homesoftlink\"'"
echo " echo ''"
Ungrep="$Ungrep|home.host.$Containeruser"
;;
*)
Line="$(convertpath container "$Line")"
[ "${Line#$Containeruserhome}" = "$Line" ] || Ungrep="$Ungrep|$(basename "$Line")"
;;
esac
}
done < <(store_runoption dump volume)
echo " echo '# Copy files from /etc/skel into empty HOME'"
echo " echo '[ -d /etc/skel ] && [ -z \"\$(ls -A \"\$Containeruserhome\" 2>/dev/null | grep -v -E \"gnupg$Ungrep\")\" ] && {'"
echo " echo ' debugnote \"$(basename $Containerrc): HOME is empty. Copying from /etc/skel\"'"
echo " echo ' cp -n -R /etc/skel/. \$Containeruserhome'"
echo " echo ' :'"
echo " echo '} || {'"
echo " echo ' debugnote \"$(basename $Containerrc): HOME is not empty. Not copying from /etc/skel\"'"
echo " echo '}'"
echo " echo ''"
[ -n "$Newdisplay" ] && {
echo " echo '# Create softlink to X unix socket'"
echo " echo '[ -e /tmp/.X11-unix/X$Newdisplaynumber ] || ln -s /X$Newdisplaynumber /tmp/.X11-unix'"
echo " echo ''"
}
[ "$Dbusrunsession" = "yes" ] && {
echo " echo '# Check for dbus user daemon command'"
echo " echo 'command -v dbus-run-session >/dev/null && Dbus=dbus-run-session || note \"Option --dbus: dbus seems to be not installed.
Cannot run a DBus user session. Please install package dbus in image.\"'"
echo " echo ''"
}
case $Xserver in
--tty)
echo " echo 'unset DISPLAY WAYLAND_DISPLAY XAUTHORITY'" ;;
--weston|--kwin|--hostwayland)
echo " echo 'unset DISPLAY XAUTHORITY'" ;;
*)
echo " echo 'unset WAYLAND_DISPLAY'" ;;
esac
echo " echo ''"
[ "$Setupwayland" = "yes" ] && {
echo " echo '# Wayland environment'"
echo " echo 'export WAYLAND_DISPLAY=$Newwaylandsocket'"
echo " echo 'ln -s /$Newwaylandsocket \$XDG_RUNTIME_DIR/$Newwaylandsocket'"
} || {
echo " echo 'export XDG_SESSION_TYPE=x11'"
}
echo " echo ''"
echo " echo ''"
echo " echo 'export TERM=xterm'"
echo " echo 'storeinfo test locale && export LANG=\"\$(storeinfo dump locale)\"'"
echo " echo '[ -e \"$Hostlocaltimefile\" ] || export TZ=$Hostutctime'"
echo " echo '[ \"\$(date -Ihours)\" != \"$(date -Ihours)\" ] && export TZ=$Hostutctime'"
echo " echo '[ \"\$DEBIAN_FRONTEND\" = noninteractive ] && unset DEBIAN_FRONTEND && export DEBIAN_FRONTEND'"
echo " echo '[ \"\$DEBIAN_FRONTEND\" = newt ] && unset DEBIAN_FRONTEND && export DEBIAN_FRONTEND'"
echo " echo '# container environment (--env)'"
while read -r Line ; do ### FIXME '\\\' not transmitted
echo " echo \"export '$Line'\""
# echo "$Line" >&2
# echo " echo \"export $(escapestring "$Line")\""
# echo " echo \"export $(escapestring "$Line")\"" >&2
# echo " echo \"export \\\"$(escapestring "$Line")\\\"\"" >&2
# echo " echo \"export \\\"$(escapestring "$Line")\\\"\""
done < <(store_runoption dump env)
echo " echo ''"
[ "$Xauthentication" = "yes" ] || echo " echo 'unset XAUTHORITY && export XAUTHORITY'"
echo " echo 'env >> $(convertpath share $Containerenvironmentfile)'"
echo " echo 'verbose \"Container environment:'"
echo " echo '\$(env | sort)\"'"
echo " echo ''"
echo " echo 'cd \"\$HOME\"'"
echo ' [ "$Workdir" ] && echo "[ -d \"$Workdir\" ] && cd \"$Workdir\" # WORKDIR in image"'
echo " echo ''"
[ "$Initsystem" = "systemd" ] && {
echo " echo 'systemctl --user start dbus'"
echo " echo ''"
}
case $Interactive in
no)
echo " echo 'tail -f $(convertpath share $Cmdstdoutlogfile) 2>/dev/null &'"
echo " echo 'tail -f $(convertpath share $Cmdstderrlogfile) >&2 2>/dev/null &'"
echo " echo \"exec \\\$Dbus sh $(convertpath share $Cmdrc) >>$(convertpath share $Cmdstdoutlogfile) 2>>$(convertpath share $Cmdstderrlogfile)\""
;;
yes)
echo " echo \"\$Exec \\\$Dbus \$Entrypoint \$Containercommand\""
;;
esac
echo "} >> $Containerrc"
echo "######## End of containerrc ########"
echo ""
echo "# Write containerrc into x11docker.log"
echo "nl -ba >> $Logfile < $Containerrc"
echo ""
echo "######## Create $(basename $Cmdrc) ########"
echo "{ echo '#! /bin/sh'"
echo " echo '# Created startscript for cmdrc containing final container command'"
echo " echo ''"
echo " echo '$(declare -f storeinfo)'"
echo " echo '$Messagefifofuncs'"
echo " echo 'Messagefile=$(convertpath share $Messagefifo)'"
# --runasuser commands added here
[ "$Runasuser" ] && {
echo " echo '# Custom daemon commands added with option --runasuser'"
for Line in "$Runasuser"; do
echo " echo 'debugnote \"$(basename $Cmdrc): Adding command:
$Line\"'"
echo " echo '$Line'"
done
echo " echo ''"
}
echo " echo \"debugnote \\\"$(basename $Cmdrc): Running container command:
\$Entrypoint \$Containercommand
\\\"\""
echo " echo ''"
echo " echo \"\$Entrypoint \$Containercommand $( [ "$Forwardstdin" = "yes" ] && echo "<$(convertpath share $Cmdstdinfifo)" ) \""
echo " echo ''"
echo " echo '[ -h \"\$Homesoftlink\" ] && rm \$Homesoftlink'"
echo " echo \"storeinfo cmdexitcode=\\\$?\""
echo "} >> $Cmdrc"
echo "######## End of cmdrc ########"
echo ""
echo "# Write cmdrc into x11docker.log"
echo "nl -ba >> $Logfile < $Cmdrc"
echo ""
# check [and create] cgroup mountpoint for systemd or elogind
[ "$Sharecgroup" = "yes" ] && [ "$Dbussystem" = "yes" ] && {
[ "$Initsystem" = "systemd" ] && Wantcgroup=systemd || Wantcgroup=elogind
findmnt /sys/fs/cgroup/$Wantcgroup >/dev/null || {
echo "# Check [and create] cgroup mountpoint for $Wantcgroup"
echo "[ '$Wantcgroup' = 'systemd' ] || $Dockerexe run --rm --entrypoint env $Imagename sh -c 'ls /lib/elogind/elogind || ls /usr/sbin/elogind|| ls /usr/libexec/elogind' && {"
echo ' [ "$(id -u)" = "0" ] && note "Creating cgroup mountpoint for '$Wantcgroup'."'
echo ' [ "$(id -u)" != "0" ] && {'
echo " note 'Want to create and mount a cgroup for $Wantcgroup.
As x11docker currently does not run as root, this will probably fail.
Please either run x11docker as root, or run with option --pw=su or --pw=sudo.
Alternatively, create cgroup mountpoint yourself with:
mkdir -p /sys/fs/cgroup/$Wantcgroup
mount -t cgroup cgroup /sys/fs/cgroup/$Wantcgroup -o none,name=$Wantcgroup
If you get a read-only error message, remove write protection with:
mount -o remount,rw cgroup /sys/fs/cgroup
You can restore write protection after cgroup creation with:
mount -o remount,ro cgroup /sys/fs/cgroup'"
[ "$Wantcgroup" = "elogind" ] && echo "note 'If you do not want or need elogind in container,
just ignore message above.'"
echo " }"
findmnt /sys/fs/cgroup -O ro >/dev/null && {
echo " mount -o remount,rw cgroup /sys/fs/cgroup >>$Containerlogfile 2>&1"
echo " Remounted=yes"
}
echo " mkdir -p /sys/fs/cgroup/elogind >>$Containerlogfile 2>&1"
echo " mount -t cgroup cgroup /sys/fs/cgroup/elogind -o none,name=elogind >>$Containerlogfile 2>&1"
echo ' [ "${Remounted:-}" = "yes" ] && mount -o remount,ro cgroup /sys/fs/cgroup >>'$Containerlogfile' 2>&1'
echo "}"
echo ""
}
}
echo "# Send signal to run X and wait for X to be ready"
echo 'storeinfo readyforX=ready'
echo "waitforlogentry 'dockerrc' $Xinitlogfile 'xinitrc is ready' '$Xiniterrorcodes'"
echo ""
echo "rocknroll || exit 64"
echo ""
[ "$Windowmanagermode" = "none" ] || {
echo "# run window manager (in image or from host)"
echo "Windowmanagermode=\"$Windowmanagermode\""
echo "Windowmanagercommand=\"$Windowmanagercommand\""
echo "Wmdockercommand=\"$Wmdockercommand\""
echo '[ "$Windowmanagermode" = "container" ] && {'
echo " $Dockerexe inspect \"\$(cut -d' ' -f1 <<<\"\$Windowmanagercommand\")\" >>$Containerlogfile 2>&1 && {"
echo ' Wmdockercommand="$Wmdockercommand \
-- $Windowmanagercommand"'
echo ' debugnote "dockerrc: Window manager container: Generated docker command:
$Wmdockercommand"'
echo " note \"Option --wm: Starting window manager image: $Windowmanagercommand\""
echo ' Wmcontainerid="$(eval $Wmdockercommand)"'
echo ' [ "$Wmcontainerid" ] && {'
echo ' debugnote "dockerrc: Window manager container: $Wmcontainerid"'
echo " for ((Count=1 ; Count<=10 ; Count++)); do"
echo " Pid1pid=\"\$($Dockerexe inspect --format '{{.State.Pid}}' \$Wmcontainerid 2>>$Containerlogfile | rmcr)\""
echo " debugnote \"dockerrc: Window manager container: \$Count. check for PID 1: \$Pid1pid\""
case $Mobyvm in
no) echo ' checkpid "$Pid1pid" && break' ;;
yes) echo ' [ "$Pid1pid" ] && [ "$Pid1pid" != "0" ] && break' ;;
esac
echo " rocknroll || exit 64"
echo " mysleep 0.2"
echo " done"
echo ' }'
echo ' checkpid "$Pid1pid" && storepid "$Pid1pid" wmcontainerpid1'
echo ' checkpid "$Pid1pid" || { note "Option --wm: Failed to run window manager image: $Windowmanagercommand." && Windowmanagermode=host ; }'
echo ' } || {'
echo " note \"Option --wm: Did not find window manager image
\$(cut -d' ' -f1 <<<\"\$Windowmanagercommand\")
to provide a containerized window manager. Please run:
docker pull x11docker/openbox
If you want to use a host window manager instead and avoid this warning,
use option --wm=host or --wm=COMMAND
or provide a local image with e.g. --wm=x11docker/fvwm
To run without a window manager: --wm=none or --desktop
Fallback: Will try to run a host window manager: $Hostwindowmanager\""
echo " Windowmanagermode=host"
echo " }"
echo "}"
echo '[ "$Windowmanagermode" = "host" ] && {'
echo " command -v $Hostwindowmanager >/dev/null || note 'Did not find a host window manager.
Please pull image x11docker/openbox or provide a recommended one:
$Wm_recommended_nodesktop_light'"
echo " note 'Option --wm: Starting host window manager: ${Hostwindowmanager:-WM_NOT_FOUND}'"
echo " [ \"\$(id -u)\" = '0' ] && su $Hostuser -c 'env $Newxenv ${Hostwindowmanager:-NO_WM_FOUND} >>$Xinitlogfile 2>&1 & storepid \$! windowmanager' || \\"
echo " env $Newxenv ${Hostwindowmanager:-NO_WM_FOUND} >>$Xinitlogfile 2>&1 & storepid \$! windowmanager"
echo '}'
}
echo ""
echo "rocknroll || exit 64"
echo ""
echo ""
echo "#### run docker image ####"
case $Interactive in
no)
# echo "read Containerid < <($Dockercommand 2>>$Containerlogfile | rmcr)"
echo "read Containerid < <($Dockercommand | rmcr)"
;;
yes)
[ "$Winpty" ] && echo "$Winpty bash $Dockercommandfile <&0 &" || echo "$Dockercommand <&0 &"
echo "Containerid=$Containername"
;;
esac
echo "##########################"
echo ""
echo ""
echo "[ \"\$Containerid\" ] || {
error \"Startup of docker failed. Did not receive a container ID.
Last lines of container log:
\$(rmcr < $Containerlogfile | tail)\"
}"
echo 'storeinfo containerid="$Containerid"'
echo "# Wait for container to be ready"
echo "for ((Count=1 ; Count<=40 ; Count++)); do"
echo " $Dockerexe exec $Containername sh -c : 2>&1 | rmcr >>$Containerlogfile && { debugnote 'dockerrc: Container is up and running.' ; break ; } || debugnote \"dockerrc: Container not ready on \$Count. attempt, trying again.\""
echo " rocknroll || exit 64"
echo " mysleep 0.1"
echo "done"
echo ""
[ "$Containersetup" = "no" ] && {
# echo "$Dockerexe logs -f \$Containerid >> $Containerlogfile 2>&1 &"
echo "# Store container output separated for stdout and stderr"
echo "$Dockerexe logs -f \$Containerid 1>>$Cmdstdoutlogfile 2>>$Cmdstderrlogfile &"
echo "Dockerlogspid=\$!"
echo "storepid \$Dockerlogspid dockerlogs"
echo ""
}
echo "# Wait for pid 1 in container"
echo "for ((Count=1 ; Count<=40 ; Count++)); do"
echo " Pid1pid=\"\$($Dockerexe inspect --format '{{.State.Pid}}' $Containername 2>>$Containerlogfile | rmcr)\""
echo " debugnote \"dockerrc: \$Count. check for PID 1: \$Pid1pid\""
case $Mobyvm in
no) echo ' checkpid "$Pid1pid" && break' ;;
yes) echo ' [ "$Pid1pid" ] && [ "$Pid1pid" != "0" ] && break' ;;
esac
echo " rocknroll || exit 64"
echo " mysleep 0.1"
echo "done"
echo '[ "$Pid1pid" = "0" ] && Pid1pid=""'
echo '[ -z "$Pid1pid" ] && error "dockerrc(): Did not receive PID of PID1 in container.
Maybe the container immediately stopped for unknown reasons.
Just in case, check if host and image architecture are compatible:
Host architecture: '$Hostarchitecture', image architecture: $Containerarchitecture.
Output of \"docker ps | grep x11docker\":
$('$Dockerexe' ps | grep x11docker)
Content of container log:
$(rmcr < '$Containerlogfile' | uniq )"'
echo 'storeinfo pid1pid="$Pid1pid"'
echo ""
echo "# Get IP of container"
echo "Containerip=\"\$($Dockerexe inspect --format '{{ .NetworkSettings.IPAddress }}' $Containername 2>>$Containerlogfile)\""
echo 'storeinfo containerip=$Containerip'
echo ""
echo "# Check log for startup failure"
echo "Failure=\"\$(rmcr < $Containerlogfile | grep -v grep | grep -E 'Error response from daemon|OCI runtime exec' ||:)\""
echo "[ \"\$Failure\" ] && {"
echo " echo \"\$Failure\" >>$Containerlogfile"
echo " error \"Got error message from docker daemon:
\$Failure
Last lines of logfile:
\$(tail $Containerlogfile)\""
echo "}"
echo ""
[ "$Switchcontaineruser" = "no" ] && [ "$Containersetup" = "yes" ] && {
echo "debugnote 'dockerrc(): Starting containerrootrc with privileged docker exec'"
echo "# copy containerrootrc inside of container to avoid possible noexec of host home."
echo "$Dockerexe exec --privileged $Containername sh -c 'cp $(convertpath share $Containerrootrc) /tmp/containerrootrc ; chmod 644 /tmp/containerrootrc' 2>&1 | rmcr >>$Containerlogfile"
echo "# run container root setup. containerrc will wait until setup script is ready."
echo "$Dockerexe exec --privileged -u root $Containername /bin/sh /tmp/containerrootrc 2>&1 | rmcr >>$Containerlogfile"
echo ""
}
echo "storeinfo dockerrc=ready"
echo ""
case $Mobyvm in
no)
echo '[ "$Containerid" ] || [ "$Wmcontainerid" ] && {'
echo " # wait for signal of finish()"
echo " read Signal <$Dockerstopsignalfifo"
echo ' [ "$Signal" = "stop" ] && {'
echo " [ \"\$Containerid\" ] && $Dockerexe stop \$Containerid >> $Containerlogfile 2>&1 &"
echo " [ \"\$Wmcontainerid\" ] && $Dockerexe stop \$Wmcontainerid >> $Containerlogfile 2>&1 &"
echo " [ \"\$Dockerlogspid\" ] && kill \$Dockerlogspid >> $Containerlogfile 2>&1 &"
echo " }"
echo "} & storepid \$! dockerstopshell"
;;
esac
echo "exit 0"
return 0
} >> $Dockerrc
create_xtermrc() { # create xtermrc: Script to prompt for password (if needed) and to run dockerrc
echo "#! /usr/bin/env bash"
echo "# Ask for password if needed."
echo "# Runs in terminal or in an additional terminal window"
echo ""
declare -f rocknroll
declare -f storeinfo
echo "$Messagefifofuncs"
echo "Timetosaygoodbyefile='$Timetosaygoodbyefile'"
echo ""
#[ "$Debugmode" = "yes" ] && echo "set -x"
echo "Messagefile='$Messagefifo'"
echo "Storeinfofile='$Storeinfofile'"
echo "export TERM=xterm SHELL=/bin/bash"
echo ""
echo "debugnote 'Running xtermrc: Ask for password if needed ($Passwordneeded)'"
echo ""
[ "$Passwordneeded" = "yes" ] && case $Passwordfrontend in
su|sudo)
echo "echo 'x11docker $Imagename $Containercommand:'"
echo "echo 'Please type in your password to run docker on display $Newdisplay'"
echo "echo -n 'Password ($Passwordfrontend): '"
;;
esac
echo ""
case $Passwordfrontend in
gksudo|lxsudo) echo "$Passwordcommand bash $Dockerrc" ;;
pkexec) echo "pkexec env DISPLAY=\$DISPLAY XAUTHORITY=\$XAUTHORITY bash $Dockerrc" ;;
gksu) echo "$Passwordcommand \"bash $Dockerrc \"" ;;
lxsu) echo "$Passwordcommand bash $Dockerrc" ;;
*) echo "$Passwordcommand \"${Sudo}bash $Dockerrc \"" ;;
esac
echo ""
echo "storeinfo xtermrc=ready"
echo "exit 0"
return 0
} >> $Xtermrc
#### final startup routines
start_compositor() { # start Wayland compositor Weston or KWin
local Compositorkeyword
case $Xserver in
--weston|--weston-xwayland|--xpra-xwayland|--xdummy-xwayland) Compositorkeyword="weston-desktop-shell" ;;
--kwin|--kwin-xwayland) Compositorkeyword="X-Server" ;;
esac
unpriv "$(command -v dbus-launch) $Compositorcommand >> $Compositorlogfile 2>&1 & echo compositorpid=\$! >>$Storeinfofile"
storeinfo "compositorpid=$(storeinfo dump compositorpid)"
waitforlogentry "start_compositor()" "$Compositorlogfile" "$Compositorkeyword" "$Compositorerrorcodes"
setonwatchpidlist "$(storeinfo dump compositorpid)" compositor
case $Xserver in
--xpra-xwayland|--xdummy-xwayland) # hide weston window
unpriv "xdotool windowunmap 0x$(printf '%x\n' $(grep 'window id' $Compositorlogfile | rev | cut -d' ' -f1 | rev))" ;;
esac
return 0
}
start_docker() { # start xtermrc -> dockerrc
# run docker in xtermrc, ask for password if needed
case $Passwordfrontend in
su|sudo)
case $Passwordneeded in
no) /usr/bin/env bash $Xtermrc ;;
yes) $Passwordterminal /usr/bin/env bash $Xtermrc ;;
esac
;;
*) $Passwordterminal "bash $Xtermrc" ;;
esac
waitforlogentry "start_docker()" "$Storeinfofile" "dockerrc=ready" "" infinity
}
start_hostexe() { # options --exe, --xonly: Run host executeable instead of docker container
local Line
# generate start script
{ echo "#! /usr/bin/env bash"
echo "# --exe: Run host executeable $Hostexe"
echo ""
#[ "$Debugmode" = "yes" ] && echo "set -Eux"
echo "$Messagefifofuncs"
declare -f pspid
declare -f storeinfo
declare -f storepid
echo ""
echo "Messagefile='$Messagefifo'"
echo "Storeinfofile='$Storeinfofile'"
echo "Storepidfile='$Storepidfile'"
echo "Tini=''"
[ "$Dbusrunsession" = "yes" ] && echo "Dbus='$(command -v dbus-run-session >/dev/null)'"
echo "export $Newxenv"
[ "$Setupwayland" = "no" ] && {
echo "unset WAYLAND_DISPLAY"
echo "unset $Waylandtoolkitvars"
}
echo ""
case $Xserver in
--weston|--kwin|--hostwayland)
echo "unset DISPLAY XAUTHORITY"
;;
esac
echo "export HOME='$Containeruserhome'"
echo "cd '$Containeruserhome'"
[ "$Workdir" ] && echo "cd '$Workdir'"
echo ""
while read Line; do
echo "export '$Line'"
done < <(store_runoption dump env | grep -v XAUTHORITY | grep -v XDG_RUNTIME_DIR)
echo ""
echo "env >> $Containerenvironmentfile"
echo "verbose \"Container environment:"
echo "\$(env | sort)\""
echo ""
[ "$Windowmanagermode" = "host" ] && {
command -v $Hostwindowmanager >/dev/null && {
echo " note 'Option --wm: Starting host window manager: ${Hostwindowmanager:-WM_NOT_FOUND}'"
echo " ${Hostwindowmanager:-NO_WM_FOUND} >>$Xinitlogfile 2>&1 & storepid \$! windowmanager"
} || note "Did not find a host window manager. Please provide one. Recommended:
$Wm_recommended_nodesktop_light"
}
echo ""
echo "storeinfo test tini && {"
echo " Tini=\"\$(storeinfo dump tini) --\" "
echo " export TINI_SUBREAPER=1"
echo "}"
echo ""
echo "# close additional file descriptors"
echo "for i in 3 4 6 7 8 9; do"
echo " { >&\$i ;} 2>/dev/null && exec >&\$i-"
echo "done"
echo ""
echo "\$Tini \$Dbus $Hostexe $( [ "$Forwardstdin" = "yes" ] && echo "<$Cmdstdinfifo") >>$Cmdstdoutlogfile 2>>$Cmdstderrlogfile &"
echo 'storeinfo pid1pid=$!'
} >> $Containerrc
nl -ba <$Containerrc >> $Containerlogfile
# Send signal to run X and wait for X to be ready
storeinfo readyforX=ready
waitforlogentry "$Xserver" "$Xinitlogfile" "xinitrc is ready" "$Xiniterrorcodes"
# run start script
unpriv "/usr/bin/env bash $Containerrc" ### FIXME support --user
return 0
}
start_pulseaudiotcp() { # option --pulseaudio=tcp: load Pulseaudio TCP module (authenticated with container IP)
local Containerip
Containerip="$(storeinfo dump containerip)"
Pulseaudiomoduleid="$(unpriv "pactl load-module module-native-protocol-tcp port=$Pulseaudioport auth-ip-acl=${Containerip:-"127.0.0.1"}" )"
[ "$Pulseaudiomoduleid" ] && {
storeinfo "pulseaudiomoduleid=$Pulseaudiomoduleid"
} || note "Option --pulseaudio: command pactl failed.
Is pulseaudio running at all on your host?
You can try option --alsa instead."
return 0
}
start_xpra() { # options --xpra / --xpra-xwayland: start and watch xpra server and xpra client
local Xpraserverpid Xpraclientpid Xpraenv
Xpraenv=" NO_AT_BRIDGE=1 \\
XPRA_EXPORT_ICON_DATA=0 \\
XPRA_EXPORT_XDG_MENU_DATA=0 \\
XPRA_ICON_OVERLAY=0 \\
XPRA_MENU_ICONS=0 \\
XPRA_UINPUT=0 \\
XPRA_XDG_EXPORT_ICONS=0 \\
XPRA_XDG_LOAD_GLOB=0 \\
$(verlt $Xpraversion v2.1 && echo XPRA_OPENGL_DOUBLE_BUFFERED=1 ||:)"
# xpra server
Xpraservercommand="env XAUTHORITY=$Xclientcookie \\
GDK_BACKEND=x11 \\
$Xpraenv $Xpraservercommand"
debugnote "Running xpra server:
$Xpraservercommand"
echo "x11docker [$(timestamp)]: Starting Xpra server" >> $Xpraserverlogfile
unpriv "$Xpraservercommand ||:" >> $Xpraserverlogfile 2>&1 &
Xpraserverpid=$!
storepid $Xpraserverpid xpraserver
verlt "$Xprarelease" "r23060" && waitforlogentry "xpra server" $Xpraserverlogfile 'xpra is ready'
rocknroll || return 64
# xpra client
Xpraclientcommand="env $Hostxenv \\
$Xpraenv $Xpraclientcommand"
debugnote "Running xpra client:
$Xpraclientcommand"
echo "x11docker [$(timestamp)]: Starting Xpra client" >> $Xpraclientlogfile
unpriv "$Xpraclientcommand ||:" >> $Xpraclientlogfile 2>&1 &
Xpraclientpid=$!
storepid $Xpraclientpid xpraclient
# catch possible xpra crashes
while rocknroll; do
ps -p $Xpraserverpid >/dev/null || break
ps -p $Xpraclientpid >/dev/null || break
sleep 1
done
sleep 1 && rocknroll && note "Option $Xserver: xpra terminated unexpectedly.
Last lines of xpra server log:
$(tail $Xpraserverlogfile)
---------------------------------
Last lines of xpra client log:
$(tail $Xpraclientlogfile)"
saygoodbye xpra
return 0
}
start_xserver() { # start X server
case $Xserver in
--xpra|--xephyr|--xdummy|--xvfb|--xwayland|--nxagent|--weston-xwayland|--kwin-xwayland|--xpra-xwayland|--xdummy-xwayland|--xwin)
unpriv "env WAYLAND_DISPLAY=$Newwaylandsocket xinit $Xinitrc -- $Xcommand >> $Xinitlogfile 2>&1 " ;;
--xorg)
case $Xlegacywrapper in
yes) unpriv " xinit $Xinitrc -- $Xcommand >> $Xinitlogfile 2>&1 " ;;
no) eval " xinit $Xinitrc -- $Xcommand >> $Xinitlogfile 2>&1 " ;;
esac
;;
--hostdisplay|--hostwayland|--weston|--kwin|--tty)
unpriv " bash $Xinitrc >> $Xinitlogfile 2>&1 " ;;
--runx) unpriv " $Xcommand -- bash $Xinitrc >> $Xinitlogfile 2>&1 " ;;
esac
[ $? != 0 ] && rocknroll && note "X server $Xserver returned an error code.
Last lines of xinit logfile:
$(tail $Xinitlogfile)
$( [ -s "$Compositorlogfile" ] && echo "Last lines of compositor log:
$(tail $Compositorlogfile)")"
return 0
}
#### main init routines
check_fallback() {
# Option --fallback
case $Fallback in
no) error "Option --fallback=no: Fallbacks are disabled.
x11docker cannot fulfil an option you have chosen, see message above." ;;
esac
}
check_host() { # check host environment
local Drive
[ "${0:-}" = "${BASH_SOURCE:-}" ] && Runssourced="no" || Runssourced="yes"
Hostsystem="$(grep '^ID=' /etc/os-release 2>/dev/null | cut -d= -f2 || echo 'unknown')"
Hostarchitecture="$(uname -m)"
case "$Hostarchitecture" in
x86_64|x86-64|amd64|AMD64) Hostarchitecture="amd64 ($Hostarchitecture)" ;;
aarch64|armv8|ARMv8|arm64v8) Hostarchitecture="arm64v8 ($Hostarchitecture)" ;;
aarch32|armv8l|armv7|armv7l|ARMv7|arm32v7|armhf|armv7hl) Hostarchitecture="arm32v7 ($Hostarchitecture)" ;;
arm32v6|ARMv6|armel) Hostarchitecture="arm32v6 ($Hostarchitecture)" ;;
arm32v5|ARMv5) Hostarchitecture="arm32v5 ($Hostarchitecture)" ;;
i686|i386|x86) Hostarchitecture="i386 ($Hostarchitecture)" ;;
ppc64*|POWER8) Hostarchitecture="ppc64le ($Hostarchitecture)" ;;
s390x) Hostarchitecture="s390x ($Hostarchitecture)" ;;
mips|mipsel) Hostarchitecture="mipsel ($Hostarchitecture)" ;;
mips64*) Hostarchitecture="mips64el ($Hostarchitecture)" ;;
*) Hostarchitecture="unknown ($Hostarchitecture)" ;;
esac
# Check libc from host. If same as in container, it is possible to share timezone file
Hostlibc="unknown"
ldd --version 2>&1 | grep -q 'musl libc' && Hostlibc='musl'
ldd --version 2>&1 | grep -q -E 'GLIBC|GNU libc' && Hostlibc='glibc'
# Check host time zone
Hostlocaltimefile="$(myrealpath /etc/localtime)" # Find time zone file in /usr/share/zoneinfo
[ -e "$Hostlocaltimefile" ] || Hostlocaltimefile=""
Hostutctime=$(date +%:::z) # Offset of UTC. Used if time zone file cannot be provided
[ "$(cut -c1 <<< "$Hostutctime")" = "+" ] && {
Hostutctime="UTC-$(cut -c2- <<< "$Hostutctime")"
} || {
Hostutctime="UTC+$(cut -c2- <<< "$Hostutctime")"
}
# Check for MS Windows subsystem
command -v cygcheck.exe >/dev/null && {
cygcheck.exe -V | rmcr | grep -q "(cygwin)" && Winsubsystem="CYGWIN"
cygcheck.exe -V | rmcr | grep -q "(msys)" && Winsubsystem="MSYS2"
}
uname -r | grep -q "Microsoft" && Winsubsystem="WSL1"
uname -r | grep -q "microsoft" && Winsubsystem="WSL2"
case $Winsubsystem in
MSYS2|CYGWIN)
Winsubmount="$(cygpath.exe -u "c:/" | rmcr | sed s%/c/%%)"
Winsubpath="$(convertpath unix "$(cygpath.exe -w "/" | rmcr)" )"
Mobyvm="yes"
;;
WSL1|WSL2)
command -v "/mnt/c/Windows/System32/cmd.exe" >/dev/null && Winsubmount="/mnt"
command -v "/c/Windows/System32/cmd.exe" >/dev/null && Winsubmount=""
grep -q "Windows" <<< "${PATH:-}" || export PATH="${PATH:-}:$Winsubmount/c/Windows/System32:$Winsubmount/c/Windows/System32/WindowsPowerShell/v1.0" # can miss after sudo in WSL
command -v "$Winsubmount/c/Windows/System32/cmd.exe" >/dev/null || error "$Winsubsystem: Could not find cmd.exe
in /mnt/c/Windows/System32 or /c/Windows/System32.
Do you have a different path to your Windows system partition?"
Winsubpath="$(convertpath unix "$(getwslpath)")"
[ "$Winsubsystem" = "WSL1" ] && Mobyvm="yes"
;;
esac
Winsubmount="${Winsubmount%/}"
Winsubpath="${Winsubpath%/}"
[ "$Winsubsystem" ] && Hostsystem="MSWindows-$Winsubsystem"
[ -z "$Mobyvm" ] && Mobyvm="no"
case $Mobyvm in
yes)
command -v docker.exe >/dev/null || export PATH="${PATH:-}:$(convertpath subsystem "C:/Program Files/docker"):$(convertpath subsystem "C:/Program Files/Docker/Docker/resources/bin")"
Dockerexe="docker.exe"
;;
no)
Dockerexe="docker"
;;
esac
[ "$Podman" = "yes" ] && Dockerexe="podman"
# Check host IP. Needed for --pulseaudio=tcp, --printer=tcp, --xoverip and --xwin
case $Winsubsystem in
"")
case $Network in
host) Hostip="127.0.0.1" ;;
*)
#Hostip="$(hostname -I | cut -d' ' -f1)"
[ "$Hostip" ] || Hostip="$(ip -4 -o a | grep 'docker0' | awk '{print $4}' | cut -d/ -f1 | grep "172.17.0.1" ||: )"
#[ "$Hostip" ] || Hostip="$($Dockerexe network inspect bridge --format='{{.IPAM.Config}}' 2>/dev/null | awk '{print $2}')"
[ "$Hostip" ] || Hostip="$(ip -4 -o a | grep 'docker0' | awk '{print $4}' | cut -d/ -f1 | head -n1)"
[ "$Hostip" ] || Hostip="$(ip -4 -o a | awk '{print $4}' | cut -d/ -f1 | grep "^192\.168\.*" | head -n1)"
[ "$Hostip" ] || Hostip="$(ip -4 -o a | awk '{print $4}' | cut -d/ -f1 | grep -v "127.0.0.1" | head -n1)"
;;
esac
;;
*)
Hostip="$(ipconfig.exe | rmcr | grep 'IPv4' | grep -o '192\.168\.[0-9]*\.[0-9]*' | head -n1 )"
[ "$Hostip" ] || Hostip="$(ipconfig.exe | rmcr | grep 'IPv4' | grep -o '10\.0\.[0-9]*\.[0-9]*' | head -n1 )"
[ "$Hostip" ] || Hostip="$(ipconfig.exe | rmcr | grep 'IPv4' | grep -o '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*' | head -n1 )"
;;
esac
# Check if docker is installed with snap/snappy
myrealpath "$(command -v "${Dockerexe:-docker_not_found}")" | grep -q snap && Runsinsnap="yes" || Runsinsnap="no"
# Provide dos->unix newline converter to unpriv() commands
export -f rmcr
# Check whether x11docker runs over SSH
pstree -ps $$ >/dev/null 2>&1 && {
pstree -ps $$ | grep -q sshd && Runsoverssh="yes" || Runsoverssh="no"
} || {
check_parent_sshd "$$" && Runsoverssh="yes" || Runsoverssh="no"
}
# Check whether x11docker runs on console
Runsonconsole="$(env LANG=C tty 2>&1)"
case "$Runsonconsole" in
"not a tty") Runsonconsole="" ;;
*)
grep -q tty <<< "$Runsonconsole" && Runsonconsole="yes" || Runsonconsole="no"
;;
esac
[ "$Winsubsystem" ] && Runsonconsole="no"
[ -z "$Runsonconsole" ] && {
[ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] && Runsonconsole="no" || Runsonconsole="yes"
debugnote "check_host(): Command tty failed. Guess if running on console: $Runsonconsole"
}
# Check whether x11docker runs in a terminal
tty -s && Runsinterminal="yes" || Runsinterminal="no"
# Check whether x11docker runs in interactive bash mode (--enforce-i)
case $- in
*i*) Runsinteractive="yes" ;;
*) Runsinteractive="no" ;;
esac
# Check whether ps can watch processes of other users
mount | grep "^proc" | grep -q "hidepid=2" && {
Hosthidepid="yes"
debugnote "check_host(): /proc is mounted with hidepid=2."
} || {
Hosthidepid="no"
}
ps aux | cut -d' ' -f1 | grep -q root && {
Hostcanwatchroot="yes"
} || {
Hostcanwatchroot="no"
case $Winsubsystem in
MSYS2|CYGWIN) Hostcanwatchroot="yes" ;;
esac
}
debugnote "check_host(): ps can watch root processes: $Hostcanwatchroot"
# Check if host uses proprietary NVIDIA driver
Nvidiaversion=$(head -n1 2>/dev/null </proc/driver/nvidia/version | awk '{ print $8 }')
#Nvidiaversion="430.14"
return 0
}
check_hostuser() { # check for unprivileged host user
# check host user, want an unprivileged one to run X server
# default behaviour:
# x11docker started as unprivileged user: starting X server as this user and create same user in container
# x11docker started as root: determine real user with $(logname), instead of root use real user like above
# x11docker started as root with --hostuser=root: root runs X server and root is container user (discouraged)
# if you want root in container, just use --user=root
# x11docker with --user=someuser container user is someuser, host user is unprivileged user $(logname)
#
# root permissions are only needed to run docker. If started unprivileged, a password prompt appears.
# user who started x11docker
Startuser="$(id -un)"
# not root? Use current user.
[ -z "$Hostuser" ] && [ "$Startuser" != "root" ] && Hostuser="$Startuser"
# root? find unprivileged user
Lognameuser="$(logname 2>/dev/null ||:)"
[ -z "$Lognameuser" ] && [ -z "$Hostuser" ] && note "Your terminal seems to be not POSIX compliant.
Command 'logname' does not return a value.
Consider to use another terminal emulator.
Fallback: Will try to check \$SUDO_USER and \$PKEXEC_UID."
[ -z "$Lognameuser" ] && [ -n "${SUDO_USER:-}" ] && Lognameuser="${SUDO_USER:-}" && [ -z "$Hostuser" ] && note "Will use \$SUDO_USER = ${SUDO_USER:-} as host user."
[ -z "$Lognameuser" ] && [ -n "${PKEXEC_UID:-}" ] && Lognameuser="${PKEXEC_UID:-}" && [ -z "$Hostuser" ] && note "Will use user with uid \$PKEXEC_UID = ${PKEXEC_UID:-} as host user."
[ -z "$Lognameuser" ] && Lognameuser="$Startuser" && [ -z "$Hostuser" ] && note "Will use \$(id -un) = $Lognameuser as host user."
# option --hostuser
[ -z "$Hostuser" ] && Hostuser=$Lognameuser
[ "$Hostuser" != "$Startuser" ] && {
[ "$Startuser" = "root" ] || error "Opion --hostuser: x11docker must run as root
to choose a host user different from user '$Startuser'."
}
getent passwd $Hostuser >/dev/null 2>&1 || {
[ -e /etc/passwd ] || warning "Your system misses /etc/passwd"
warning "Could not find user '$Hostuser' in /etc/passwd."
}
Hostuser=$(id -un $Hostuser)
Hostuseruid=$(id -u $Hostuser)
Hostusergid=$(id -g $Hostuser)
[ "$Hostuser" = "$Startuser" ] && Hostuserhome="$HOME"
[ -z "$Hostuserhome" ] && Hostuserhome=$(getent passwd $Hostuser 2>/dev/null | cut -d: -f6)
[ -z "$Hostuserhome" ] && {
Hostuserhome="/tmp/home/$Hostuser"
mkdir -p "$Hostuserhome"
warning "Could not read your home directory from /etc/passwd for user '$Hostuser'.
Please set \$HOME with a valid path.
Fallback: setting HOME=$Hostuserhome"
check_fallback
}
debugnote "host user: $Hostuser $Hostuseruid:$Hostusergid $Hostuserhome"
[ "$Hostuser" = "root" ] && warning "Running as user root.
Maybe \$(logname) did not provide an unprivileged user.
Please use option --hostuser=USER to specify an unprivileged user.
Otherwise, new X server runs as root, and container user will be root."
id | grep -q "(docker)" && warning "User $Hostuser is member of group docker.
That allows unprivileged processes on host to gain root privileges."
# How to run as unprivileged user in unpriv()
case "$Hostuser" in
"$Startuser") Unpriv="eval" ;; # alternatively: bash -c
*) Unpriv="su $Hostuser -c" ;;
esac
return 0
}
check_hostxenv() { # check environment variables for host X display
Hostdisplay="${DISPLAY:-}"
Hostdisplaynumber="$(echo $Hostdisplay | cut -d: -f2 | cut -d. -f1)" # display number without ":" and ".0"
[ -n "$Hostdisplay" ] && Hostxsocket="/tmp/.X11-unix/X$Hostdisplaynumber" || Hostxsocket="" # X socket from host, needed for --hostdisplay
[ -e "$Hostxsocket" ] || Hostxsocket="" # can miss in SSH session
# Check whether host X server has MIT-SHM enabled.
command -v xdpyinfo >/dev/null && xdpyinfo >/dev/null 2>&1 && {
xdpyinfo | grep -q "MIT-SHM" && Hostmitshm="yes" || Hostmitshm="no"
}
[ "$Winsubsystem" ] && Hostmitshm="no"
# get cookie from host display
XAUTHORITY=${XAUTHORITY:-}
[ -z "$XAUTHORITY" ] && command -v systemctl >/dev/null && XAUTHORITY="$(systemctl --user show-environment | grep XAUTHORITY= | cut -d= -f2)"
[ -z "$XAUTHORITY" ] && [ -e "$Hostuserhome/.Xauthority" ] && XAUTHORITY="$Hostuserhome/.Xauthority"
[ "$Runsoverssh" = "yes" ] && [ -e "$Hostuserhome/.Xauthority" ] && XAUTHORITY="$Hostuserhome/.Xauthority"
[ "${XAUTHORITY:-}" ] && {
unpriv "xauth -i -f ${XAUTHORITY:-} nlist $Hostdisplay 2>/dev/null | xauth -f $Hostxauthority nmerge - 2>/dev/null"
chown $Hostuser $Hostxauthority
chmod 600 $Hostxauthority
export XAUTHORITY
} || {
Hostxauthority=""
unset XAUTHORITY
}
[ "$Hostdisplay" ] || {
Hostxsocket=""
Hostxauthority=""
XAUTHORITY=""
}
[ -s "${XAUTHORITY:-}" ] && [ ! -s "$Hostxauthority" ] && cp "${XAUTHORITY:-}" "$Hostxauthority"
# create $Hostxenv
Hostxenv="DISPLAY=$Hostdisplay"
[ -s "$Hostxauthority" ] && {
Hostxenv="$Hostxenv XAUTHORITY=$Hostxauthority"
export XAUTHORITY=$Hostxauthority
} || {
Hostxauthority=
unset XAUTHORITY
}
[ -n "$Hostxsocket" ] && Hostxenv="$Hostxenv XSOCKET=$Hostxsocket"
[ -n "$Hostwaylandsocket" ] && Hostxenv="$Hostxenv WAYLAND_DISPLAY=$Hostwaylandsocket"
Hostxenv="$Hostxenv XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
[ -n "$Hostdisplay" ] && [ -z "$Hostxauthority" ] && warning "Your host X server runs without cookie authentication."
[ -z "$GDK_BACKEND" ] && {
[ -n "$Hostwaylandsocket" ] && export GDK_BACKEND="wayland"
[ -n "$Hostdisplay" ] && export GDK_BACKEND="x11"
[ -z "$Hostdisplay$Hostwaylandsocket" ] && unset GDK_BACKEND
}
return 0
}
check_option_interferences() { # check multiple option interferences, change settings if needed
local Message
[ "$Desktopmode" = "no" ] && case $Xserver in
--xephyr|--weston-xwayland|--kwin-xwayland|--xorg) Windowmanagermode="auto" ;;
esac
case $Xserver in
--xorg) # check if --xorg can run
[ "$Autochooseserver" = "yes" ] && [ "$Codename" = "xonly" ] && error "Will not run an empty Xorg in auto-choosing mode.
If you want this, please use option --xorg explicitly."
[ -e "/etc/X11/Xwrapper.config" ] && sed 's/ //g' /etc/X11/Xwrapper.config | grep -xq "allowed_users=anybody" && sed 's/ //g' /etc/X11/Xwrapper.config | grep -xq "needs_root_rights=yes" && {
Xlegacywrapper="yes"
} || {
Xlegacywrapper="no"
[ "$Startuser" != "root" ] && [ "$Runsonconsole" != "yes" ] && warning "Your configuration seems not to allow to start
a second core Xorg server from within X. Option --xorg may fail.
(Per default, only root or console users are allowed to run an Xorg server).
Possible solutions:
1.) Install one of nested X servers 'Xephyr', 'Xnest' or 'nxagent'.
For --gpu support: install 'weston' and 'Xwayland'.
2.) Switch to console tty1...tty6 with <CTRL><ALT><F1>...<F6>
and start x11docker there.
3.) Run x11docker as root.
4.) Edit file '/etc/X11/Xwrapper.config' and replace line:
allowed_users=console
with lines
allowed_users=anybody
needs_root_rights=yes
If the file does not exist already, you can create it.
On Debian and Ubuntu you need package xserver-xorg-legacy.
Be aware that switching directly between Xorg servers can crash them.
Always switch to a black console first before switching to Xorg."
}
;;
--xpra) # check vfb for xpra
{ [ -z "$Xpravfb" ] || [ "$Xpravfb" = "Xvfb" ] ; } && ! command -v Xvfb >/dev/null && note "Option --xpra: Xvfb not found.
Will try to use dummy video driver Xdummy.
If you encounter xpra startup errors, please install 'Xvfb'.
$Wikipackages" && Xpravfb="Xdummy"
[ "$Xpravfb" ] || { command -v Xvfb >/dev/null && Xpravfb="Xvfb" || Xpravfb="Xdummy" ; }
;;
--tty)
[ "$Interactive" = "no" ] && {
tput lines >/dev/null 2>&1 && store_runoption env "LINES=$(tput lines)"
tput cols >/dev/null 2>&1 && store_runoption env "COLUMNS=$(tput cols)"
#verbose "Option --tty: Setting LINES and COLUMNS to terminal size ${COLUMNS}x${LINES}."
}
;;
--hostdisplay)
[ "$Winsubsystem" ] && Trusted="yes" || Trusted="no"
[ -n "$(cut -d: -f1 <<< "$Hostdisplay")" ] && Xoverip="yes"
[ -z "$Hostxauthority" ] && {
note "Option --hostdisplay: You host X server seems to run
without cookie authentication. Cannot set up a cookie for X access.
Fallback: Enabling option --no-auth."
check_fallback
Xauthentication="no"
}
case $Xtest in
yes|no)
note "Option --xtest: Cannot enable or disable X extension XTEST
with option --hostdisplay."
Xtest=""
;;
esac
;;
--xdummy|--xvfb)
Showdisplayenvironment="yes"
;;
esac
case $Xserver in
-xpra|--xpra-xwayland)
# check for version with cookie bug
! verlt "$Xpraversion" "v2.3" && verlt "$Xprarelease" "r19606" && {
command -v xhost >/dev/null || {
warning "Your xpra version has a cookie authentication issue.
also, 'xhost' is not available on your host.
Fallback: Disabling cookie authentication on new X server."
check_fallback
Xauthentication="no"
}
} ;;
esac
# check if a host window manager is needed
[ "$Desktopmode" = "no" ] && [ -z "$Windowmanagermode" ] && case $Xserver in
--xephyr|--weston-xwayland|--kwin-xwayland|--xorg|--xwayland)
note "Option $Xserver: x11docker assumes that you need
a window manager. If you don't want this, run with option --desktop.
Enabling option --wm to provide a window manager."
Windowmanagermode="auto"
[ "$Autochooseserver" = "yes" ] && [ "$Runsonconsole" = "no" ] && {
case $Sharegpu in
no) note "Did not find a nice solution to run a seamless application
on your desktop. (Only insecure option --hostdisplay would work).
It is recommended to install xpra or nxagent
to allow a seamless mode without the need of a window manager from host." ;;
yes) note "Did not find a nice solution to run a seamless application with
option --gpu on your desktop. (Only insecure option --hostdisplay would work).
It is recommended to install xpra, weston, Xwayland and xdotool
to allow a seamless mode without the need of a window manager from host." ;;
esac
}
;;
*)
Windowmanagermode="none"
;;
esac
# check xauth
[ "$Xauthentication" = "yes" ] && case $Xserver in
--tty) ;;
--weston|--kwin|--hostwayland) Xauthentication="no" ;;
*)
command -v xauth >/dev/null || {
case $Xoverip in
yes)
[ -z "$Hostxauthority" ] && [ "$Xserver" = "--hostdisplay" ] && Message=warning || Message=error
$Message "Command 'xauth' not found.
SECURITY RISK!
Your X server would be accessable over network without authentication!
That could be abused to take control over your system.
Please install 'xauth' to allow X cookie authentication.
You can disable cookie authentication with discouraged option --no-auth."
;;
no|"")
warning "Command 'xauth' not found.
Please install 'xauth' to allow X cookie authentication.
Securing X access with cookie authentication is not possible.
Fallback: Disabling X authentication protocol. (option --no-auth)
$Wikipackages"
check_fallback
;;
esac
Xauthentication="no"
}
;;
esac
# --fullscreen is nonsense on tty at all. Avoids weston error on tty.
[ "$Runsonconsole" = "yes" ] && Fullscreen="no"
# --gpu
[ "$Sharegpu" = "yes" ] && {
warning "Option --gpu degrades container isolation.
Container gains access to GPU hardware.
This allows reading host window content (palinopsia leak)
and GPU rootkits (compare proof of concept: jellyfish)."
case $Xoverip in
yes)
[ "$Network" != "host" ] && note "Option --gpu: With X over IP the host network stack must
be shared to allow GPU access. Enabling option --network=host."
Network="host"
;;
esac
}
# --hostdisplay --gpu
[ "$Xserver" = "--hostdisplay" ] && [ "$Sharegpu" = "yes" ] && [ "$Trusted" = "no" ] && {
note "Option --gpu: To allow GPU acceleration with --hostdisplay,
x11docker will allow trusted cookies."
Trusted="yes"
}
# --hostdisplay with SSH
[ "$Xserver" = "--hostdisplay" ] && [ "$Runsoverssh" = "yes" ] && {
[ "$Trusted" = "no" ] || [ "$Network" != "host" ] && {
note "For SSH connection with option --hostdisplay
x11docker must enable option --network=host and allow trusted cookies.
It is recommended to use other X server options
like --xpra, --xephyr or --nxagent."
Network="host"
Trusted="yes"
}
}
# --hostdisplay with untrusted cookies: check xdpyinfo
[ "$Xserver" = "--hostdisplay" ] && [ "$Trusted" = "no" ] && {
command -v xdpyinfo >/dev/null && {
xdpyinfo | grep -q SECURITY || {
note "Your X server does not support untrusted cookies.
Have to allow trusted cookies.
Consider to use options --xpra or --nxagent instead of --hostdisplay."
Trusted="yes"
}
} || note "Command 'xdpyinfo' not found. Need it to check
whether Xorg supports untrusted cookies for --hostdisplay
and whether extension MIT-SHM for shared memory is enabled.
Please install 'xdpyinfo'.
$Wikipackages"
}
# --clipboard
case "$Shareclipboard" in
yes)
case $Xserver in
--weston|--kwin) note "Sharing clipboard with $Xserver is not supported" ;;
--hostwayland) note "Sharing clipboard may or may not work.
Cannot enable or disable it, it depends on your Wayland compositor." ;;
--hostdisplay)
[ "$Trusted" = "no" ] && warning "Option --clipboard: To allow clipboard sharing with
option --hostdisplay, trusted cookies will be enabled.
No protection against X security leaks is left!
Consider to use another X server option."
Trusted="yes"
;;
esac
case $Xserver in
--xpra|--xpra-xwayland|--hostdisplay|--kwin|--weston|--tty|--xwin) ;;
*) note "Sharing picture clips with option --clipboard
is only possible with options --xpra, --xpra-xwayland and --hostdisplay." ;;
esac
;;
esac
[ "$Trusted" = "no" ] && warning "Clipboard isolation may fail."
case $Containersetup in
no)
case $Initsystem in
none|tini) ;;
*) note "Option --no-setup: Option --init is not supported" ;;
esac
Initsystem="none"
Dbusrunsession="no"
#Createcontaineruser="no"
Langwunsch=""
Noentrypoint="no"
Runasroot=""
# --stdin?
# --sudouser?
# --hostdbus
# nvidia installer
;;
esac
# --dbus [=system]
case $Dbusrunsession in
yes|user|session) Dbusrunsession="yes" ;;
no) ;;
system)
Dbusrunsession="yes"
Dbussystem="yes"
;;
*)
note "Option --dbus: Unknown argument '$Dbusrunsession'.
Fallback: Enabling --dbus user session."
check_fallback
Dbusrunsession="yes"
;;
esac
# --cap-default
[ "$Capdropall" = "no" ] && {
warning "Option --cap-default disables security hardening
for containers done by x11docker. Default docker capabilities are allowed.
This is considered to be less secure."
[ "$Allownewprivileges" = "auto" ] && {
note "Option --cap-default: Enabling option --newprivileges.
You can avoid this with --newprivileges=no"
Allownewprivileges="yes"
}
}
# --newprivileges
case $Allownewprivileges in
yes|no|auto) ;;
*)
note "Option --newprivileges: Unknown argument '$Allownewprivileges'.
Fallback: Setting --newprivileges=auto"
check_fallback
Allownewprivileges="auto"
;;
esac
# --hostipc: Check auto-enabling
[ "$Xserver" = "--hostdisplay" ] && [ "$Trusted" = "yes" ] && [ "$Hostmitshm" = "yes" ] && [ "$Sharehostipc" = "no" ] && [ "$Runsoverssh" = "no" ] && {
note "Option --hostdisplay: To allow --hostdisplay with trusted cookies,
x11docker must share host IPC namespace with container (option --hostipc)
to allow shared memory for X extension MIT-SHM."
Sharehostipc="yes"
}
# --scale
[ "$Scaling" ] && {
case $Xserver in
--weston|--weston-xwayland)
[[ $Scaling =~ ^[1-9]$ ]] || {
note "The scale factor for option $Xserver must be
one of 1 2 3 4 5 6 7 8 9
Fallback: disabling option --scale"
check_fallback
Scaling=""
}
;;
--xpra|--xpra-xwayland|--xorg)
isnum $Scaling || {
note "Option --scale needs a number. '$Scaling' is not allowed.
Fallback: disabling option --scale"
check_fallback
Scaling=""
}
;;
*)
note "Option $Xserver does not support option --scale.
Available for --xpra, --xpra-xwayland and --xorg (float values possible)
and for --weston and --weston-xwayland (full integer values only).
Fallback: disabling option --scale"
check_fallback
Scaling=""
;;
esac
case $Xserver in
--xpra|--xpra-xwayland)
verlt "$Xpraversion" "v0.16" && {
note "Your xpra version is quite old and does not support --scale.
You need at least xpra version 0.16
Fallback: disabling option --scale"
check_fallback
Scaling=""
}
;;
esac
case $Xserver in
--weston-xwayland) note "Weston does not work well with Xwayland in scaled mode.
In summary, Xwayland does not get the right screen resolution from Weston.
(Bug report at https://bugzilla.redhat.com/show_bug.cgi?id=1498669 ).
Try out if it works for you. Otherwise, you can combine
'--xpra-xwayland --desktop --scale $Scaling' for better desktop scaling support.
--scale for single applications works best with --xpra / --xpra-xwayland.
--scale in desktop mode works best with option --xorg."
;;
--xpra-xwayland)
[ "1" = "$(awk -v a="${Scaling:-1}" 'BEGIN {print (a < 1)}')" ] && {
command -v weston >/dev/null || {
note "Option --xpra-xwayland needs weston
for scale factor smaller than 1.
Fallback: disabling option --scale"
check_fallback
Scaling=""
}
}
;;
--xorg)
[ "1" = "$(awk -v a="$Scaling" 'BEGIN {print (a < 1)}')" ] && [ -n "$Rotation" ] && note "--xorg does not work well with combination
of --scale smaller than 1 and rotation diferent from 0."
;;
esac
}
# --rotate
[ -n "$Rotation" ] && {
case $Xserver in
--weston|--weston-xwayland|--xorg)
echo "0 90 180 270 flipped flipped-90 flipped-180 flipped-270" | grep -q "$Rotation" || { # fuzzy test, have been lazy
note "Unsupported value '$Rotation' for option --rotate.
Must be one of 0 90 180 270 flipped flipped-90 flipped-180 flipped-270
Fallback: disabling option --rotate"
check_fallback
Rotation=""
}
;;
*)
note "Option $Xserver does not support option --rotate.
Rotation is possible for --xorg, --weston and --weston-xwayland.
Fallback: disabling option --rotate"
check_fallback
Rotation=""
;;
esac
}
[ "$Rotation" = "0" ] && Rotation="normal"
# xrandr: --scale --size --rotate
command -v xrandr >/dev/null || case $Xserver in
--xorg) { [ "$Scaling" ] || [ -n "$Rotation" ] || [ -n "$Screensize" ] ; } && note "Option --xorg needs 'xrandr' to support
options --size, --scale and --rotate.
Please install 'xrandr'.
$Wikipackages"
;;
esac
# --dpi
[ -n "$Dpi" ] && case $Xserver in
--weston|--kwin|--hostwayland|--hostdisplay)
note "Option --dpi has no effect with option $Xserver"
Dpi=
;;
esac
# --output-count
[ "$Outputcount" != "1" ] && {
case $Xserver in
--xephyr|--weston|--kwin|--weston-xwayland|--kwin-xwayland|--xwin)
[[ "$Outputcount" =~ ^[1-9]$ ]] || {
note "Option --output-count: Value must be one of 1 2 3 4 5 6 7 8 9
Disabling invalid value $Outputcount"
Outputcount="1"
}
[ "$Runsonconsole" = "yes" ] && {
note "Option --outputcount only works in nested/windowed mode,
but not on tty. Fallback: disabling --outputcount"
check_fallback
Outputcount="1"
}
;;
*) note "$Xserver does not support option --output-count.
Only available for Weston, KWin and Xephyr, thus for options
--weston, --weston-xwayland, --kwin, --kwin-xwayland, --xephyr."
Outputcount="1"
;;
esac
}
# --xfishtank: fish tank
[ "$Xfishtank" = "yes" ] && {
command -v xfishtank >/dev/null || {
note "xfishtank not found. Can not show a fish tank.
Please install 'xfishtank' for option --xfishtank to show a fish tank.
$Wikipackages"
Xfishtank="no"
}
case $Xserver in
--xpra|--xpra-xwayland|--nxagent)
[ "$Desktopmode" = "no" ] && [ -z "$Windowmanagermode" ] && Windowmanagermode="auto" && Desktopmode="yes" ;;
--weston|--kwin|--hostwayland|--hostdisplay|--tty)
note "Option --xfishtank is not supported for $Xserver."
Xfishtank="no"
;;
esac
}
# MSYS2, Cygwin, WSL
case $Winsubsystem in
WSL2) note "WSL2 support is experimental and barely tested yet.
Feedback and bug reports are appreciated!" ;;
esac
case $Mobyvm in
yes)
case "$Winsubsystem" in
WSL1|WSL2)
grep -q "/c/" <<< "$Cachebasefolder" && [ -z "$Hosthomebasefolder" ] && note "With MobyVM and WSL x11docker stores its cache files on drive C:
to allow cache file sharing.
Your Docker setup might not allow to share files from drive C:.
If startup fails with an 'access denied' error,
please either allow access to drive C: or specify a custom folder for
cache storage with option '--cachebasedir D:/some/cache/folder'.
Same issue can occur with option '--home'.
Use option '--homebasedir D:/some/home/folder' in that case."
;;
esac
[ "$Initsystem" = "systemd" ] && {
note "Option --init=systemd is not supported with MobyVM.
You can try another init option instead, e.g. --init=openrc.
Fallback: Disabling option --init=systemd"
check_fallback
Initsystem="tini"
}
[ "$Sharecgroup" = "yes" ] && {
note "Option --sharecgroup is not supported with MobyVM.
Fallback: Disabling option --sharecgroup."
Sharecgroup="no"
}
;;
esac
case $Winsubsystem in
MSYS2|CYGWIN|WSL1|WSL2)
[ "$Pulseaudiomode" ] && {
note "Option --pulseaudio is not supported on MS Windows.
Fallback: Disabling option --pulseaudio"
check_fallback
Pulseaudiomode=""
}
case $Xserver in
--xwin) note "Windows firewall settings can forbid application access
to the X server. If no application window appears, but no obvious error
is shown, please check your firewall settings. Compare issue #108 on github." ;;
esac
;;
esac
# check XDG_RUNTIME_DIR
case $Xserver in
--weston|--kwin|--weston-xwayland|--kwin-xwayland)
[ -z "$XDG_RUNTIME_DIR" ] && [ -e "/run/user/${Hostuseruid:-unknownuid}" ] && export XDG_RUNTIME_DIR="/run/user/$Hostuseruid"
[ -z "$XDG_RUNTIME_DIR" ] && {
export XDG_RUNTIME_DIR="$Cachefolder/XDG_RUNTIME_DIR"
unpriv "mkdir -p $XDG_RUNTIME_DIR"
unpriv "chmod 700 $XDG_RUNTIME_DIR"
}
;;
esac
# --wayland
[ "$Setupwayland" = "yes" ] && case $Xserver in
--weston|--kwin|--hostwayland) ;;
*)
note "Option --wayland: Sharing Wayland socket is not supported
for X server option $Xserver.
You can try --weston, --kwin or --hostwayland instead.
Fallback: Disabling option --wayland."
check_fallback
Setupwayland="no"
;;
esac
[ "$Setupwayland" = "yes" ] && {
Dbusrunsession="yes"
for Line in $Waylandtoolkitenv; do
store_runoption env $Line
done
}
# check --westonini
[ -n "$Customwestonini" ] && [ ! -e "$Customwestonini" ] && {
warning "Custom weston.ini (option --westonini) not found.
$Customwestonini"
Customwestonini=""
}
# --interactive
case $Interactive in
yes)
case $Winsubsystem in
MSYS2|CYGWIN|WSL1)
Winpty="$(command -v winpty)"
Winpty="$(escapestring "$Winpty")"
[ "$Winpty" ] || error "Option -i, --interactive: On MS Windows you need 'winpty'
to run x11docker in interactive mode. MSYS2 provides winpty as a package.
On Cygwin it can be compiled from source. WSL1 isn't supported yet.
WSL2 might work, but is not tested yet."
;;
esac
[ "$Forwardstdin" = "yes" ] && {
note "You cannot use --stdin along with --interactive.
Fallback: Disabling option --stdin."
check_fallback
Forwardstdin="no"
}
[ "$Runsinteractive" = "yes" ] && {
note "Option -i, --interactive: Does not work in interactive
bash mode (option --enforce-i).
Fallback: Disabling option --interactive."
check_fallback
Interactive="no"
}
case $Initsystem in
systemd|openrc|runit|sysvinit) note "Option --interactive: Interactive mode with option
--init=$Initsystem is not well integrated yet.
Shells do not have job control and CTRL-C can behave different than expected." ;;
esac
;;
esac
[ "$Interactive" = "yes" ] && Showcontaineroutput="no"
# --limit N
[ "$Limitresources" ] && {
[ "1" = "$(awk -v a=$Limitresources "BEGIN {print (a <= 1)}")" ] && [ "1" = "$(awk -v a=$Limitresources "BEGIN {print (a > 0)}")" ] || {
warning "Option --limit: Specified value $Limitresources is out of range.
Allowed is a factor greater than 0 and less than or equal to 1. 0<FACTOR<=1
Fallback: Setting limit factor to --limit=0.5"
check_fallback
Limitresources="0.5"
}
note "Option --limit does not avoid possibly flooding the hard disk
in docker's container partition or in shared folders.
It only restricts memory and CPU usage."
}
# --keymap: XKB keyboard layount
[ -n "$Xkblayout" ] && {
case $Xserver in
--kwin|--kwin-xwayland)
[ "$Runsonconsole" = "yes" ] && note "Option --keymap does not work with option $Xserver
if running from console."
;;
esac
[ "$Xkblayout" = "clone" ] && case $Xserver in
--nxagent) ;;
*) Xkblayout="" ;;
esac
}
# --pulseaudio
case $Pulseaudiomode in
"") ;;
auto|tcp|socket)
command -v pactl >/dev/null || {
note "Option --pulseaudio: pactl not found.
Is pulseaudio installed and running on your host system?
Fallback: Disabling --pulseaudio, enabling option --alsa"
check_fallback
Pulseaudiomode=""
Sharealsa="yes"
}
;;
*)
note "Option --pulseaudio: Unknown pulseaudio mode: $Pulseaudiomode
Allowed are --pulseaudio=socket, --pulseaudio=tcp or --pulseaudio=auto.
Fallback: Enabling --pulseaudio=auto"
check_fallback
Pulseaudiomode="auto"
;;
esac
# --printer
case $Sharecupsmode in
auto)
Sharecupsmode="socket"
[ "$Runsinsnap" = "yes" ] && Sharecupsmode="tcp"
[ "$Runtime" = "kata-runtime" ] && Sharecupsmode="tcp"
;;
""|socket|tcp) ;;
*)
note "Option --printer: Invalid argument $Sharecupsmode
Fallback: Setting --printer=socket"
check_fallback
Sharecupsmode="socket"
;;
esac
# --pull
case "$Pullimage" in
yes|no|always|ask) ;;
*) note "Option --pull: Invalid argument: $Pullimage
Allowed arguments: yes|no|always|ask
Fallback: Setting --pull=ask"
check_fallback
;;
esac
case "$Runtime" in
""|runc|crun|oci) ;;
nvidia) Sharegpu="yes" ;;
kata-runtime)
note "Option --runtime=kata-runtime: Be aware not to share
the same files with runc and kata-runtime containers at the same time.
Otherwise container startup may fail."
[ "$Sharealsa" = "yes" ] && {
note "Option --alsa: ALSA sound is not possible with
--runtime=kata-runtime. Fallback: Enabling option --pulseaudio."
Sharealsa="no"
Pulseaudiomode="tcp"
}
[ "$Sharewebcam" = "yes" ] && {
note "Option --webcam: Webcam support does not work with
--runtime=kata-runtime. Fallback: Disabling option --webcam."
check_fallback
Sharewebcam="no"
}
[ "$Network" = "host" ] && {
note "Option --network=host: Sharing host network stack does not work
with --runtime=kata-runtime. Fallback: Setting --network=bridge."
check_fallback
Network="bridge"
}
[ "$Sharehostipc" = "yes" ] && {
note "Option --hostipc: Only IPC of the the qemu VM is shared with
--runtime=kata-runtime."
}
;;
*)
note "Option --runtime: x11docker does not know runtime: $Runtime"
;;
esac
case "$Podman" in
yes)
# /proc/sys/kernel/unprivileged_userns_clone might exist on debian only.
# https://github.com/mviereck/x11docker/issues/255#issuecomment-758014962
[ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" = "0" ] && error "Option --podman: Linux kernel disallows unprivileged
user namespace setup. Please run as root:
sysctl -w kernel.unprivileged_userns_clone=1"
store_runoption cap "CHOWN"
;;
esac
# Docker installed in Ubuntu snap
[ "$Runsinsnap" = "yes" ] && {
note "Docker was installed with snap. That causes some restrictions.
Option --newprivileges=yes is enabled.
Option --hostdisplay is not available because X can only be accessed over TCP.
Option --gpu only works with --xorg and --hostnet.
It is recommended to install Docker natively instead of running it in snap."
[ "$Allownewprivileges" = "auto" ] && Allownewprivileges="yes"
}
return 0
}
check_passwordfrontend() { # check password prompt frontend (pkexec, su, sudo, ...) (also option --pw)
# check if x11docker can run docker without prompting for password
[ "$Passwordfrontend" = "none" ] && Passwordneeded="no"
[ "$X11dockermode" = "exe" ] && Passwordneeded="no"
case $Mobyvm in
yes) Passwordneeded="no" && Passwordfrontend="none" ;;
esac
[ -z "$Passwordfrontend" ] && $Dockerexe info >/dev/null 2>&1 && Passwordfrontend="none" && Passwordneeded="no"
[ -z "$Passwordfrontend" ] && sudo -n env >/dev/null 2>&1 && Passwordfrontend="sudo" && Passwordneeded="no"
# check sudo. Check is not reliable, compare https://unix.stackexchange.com/questions/383918/su-or-sudo-how-to-know-which-one-will-work
### FIXME: just guessing that members of group sudo or wheel are allowed to run commands docker and env as root
[ -z "$Passwordfrontend" ] && { sudo -ln $Dockerexe >/dev/null 2>&1 || id | grep -q '(sudo)' || id | grep -q '(wheel)' ; } && command -v sudo >/dev/null && {
[ -z "$Hostdisplay$Newdisplay" ] && Passwordfrontend="sudo"
sudo -ln env >/dev/null 2>&1 || id | grep -q '(sudo)' || id | grep -q '(wheel)' && {
[ -z "$Passwordfrontend" ] && [ "$Runsinterminal" = "yes" ] && Passwordfrontend="sudo"
[ -z "$Passwordfrontend" ] && command -v gksudo >/dev/null && Passwordfrontend="gksudo"
[ -z "$Passwordfrontend" ] && command -v lxsudo >/dev/null && Passwordfrontend="lxsudo"
[ -z "$Passwordfrontend" ] && command -v kdesudo >/dev/null && Passwordfrontend="kdesudo"
}
[ -z "$Passwordfrontend" ] && Passwordfrontend="sudo"
}
# check su
[ -n "$Hostdisplay$Newdisplay" ] && {
[ -z "$Passwordfrontend" ] && [ "$Runsinterminal" = "yes" ] && Passwordfrontend="su"
[ -z "$Passwordfrontend" ] && command -v gksu >/dev/null && Passwordfrontend="gksu"
[ -z "$Passwordfrontend" ] && command -v lxsu >/dev/null && Passwordfrontend="lxsu"
[ -z "$Passwordfrontend" ] && command -v kdesu >/dev/null && Passwordfrontend="kdesu"
[ -z "$Passwordfrontend" ] && command -v beesu >/dev/null && Passwordfrontend="beesu"
}
[ -z "$Passwordfrontend" ] && Passwordfrontend="su" # default if everything else fails
# Passwordcommand: prefix to start dockerrc. Sudo: prefix to start docker in dockerrc
case $Passwordfrontend in
pkexec|"") Passwordcommand="bash -c" ; Passwordterminal="bash -c" ;;
su) Passwordcommand="su -c" ;;
sudo) Passwordcommand="bash -c" ; Sudo="sudo -E " ;;
gksu) Passwordcommand="gksu --message 'x11docker $Imagename' --disable-grab" ; Passwordterminal="bash -c" ;;
gksudo) Passwordcommand="gksudo --message 'x11docker $Imagename' --disable-grab" ; Passwordterminal="bash -c" ;;
lxsu) Passwordcommand="lxsu" ; Passwordterminal="bash -c" ;;
lxsudo) Passwordcommand="lxsudo" ; Passwordterminal="bash -c" ;;
kdesu) Passwordcommand="kdesu -c" ; Passwordterminal="bash -c" ;;
kdesudo) Passwordcommand="kdesudo --comment 'x11docker $Imagename'" ; Passwordterminal="bash -c" ;;
beesu) Passwordcommand="beesu -c" ; Passwordterminal="bash -c" ;;
none) Passwordcommand="bash -c" ; Passwordterminal="bash -c" ;;
*) warning "Unknown password prompt '$Passwordfrontend' (option --pw).
Possible: su sudo gksu gksudo lxsu lxsudo kdesu kdesudo beesu pkexec none"
Passwordcommand="$Passwordfrontend" ; Passwordterminal="bash -c" ;;
esac
[ "$Passwordneeded" = "yes" ] && {
command -v $(echo $Passwordcommand|cut -d' ' -f1) >/dev/null || {
warning "Password prompt frontend $(echo $Passwordcommand|cut -d' ' -f1) not found.
Fallback: using no password prompt (--pw=none)."
check_fallback
Passwordcommand="bash -c" ; Passwordfrontend="none" ; Passwordneeded="no" ; Passwordterminal="bash -c"
}
}
[ "$Passwordcommand" = "bash -c" ] && Passwordcommand="eval"
return 0
}
check_runmode() { # check run/--exe/--xonly
# Basically x11docker divides between
# default: run docker image
# --exe: run host executeable
# --xonly: run X server only (changes here to --exe with sleep)
#
[ -z "$Imagename" ] && X11dockermode="xonly"
case $X11dockermode in
run)
Imagebasename="$(echo $Imagename | tr / - | cut -d: -f1)"
Codename="$Imagename $Containercommand"
command -v $Dockerexe >/dev/null || error "docker is not installed.
To run docker images, you need to install docker.
$Dockerexe"
verbose "Image name: $Imagename
Container command: $Containercommand"
;;
exe)
Hostexe="$Imagename $Containercommand"
[ "$Customdockeroptions" ] && Hostexe="$Customdockeroptions -- $Hostexe" # might be a command like 'grep -- 'expr'
Imagename=""
Containercommand=""
Imagebasename="$(basename "$Hostexe" | cut -d' ' -f1)"
Codename="$Hostexe"
command -v $Hostexe >/dev/null || error "Command '$Hostexe' not found."
verbose "Host application to execute: $Hostexe"
Runsinsnap="no"
;;
xonly)
X11dockermode="exe"
Hostexe="sleep infinity"
Imagename=""
Containercommand=""
Codename="xonly"
Imagebasename="xonly"
Showdisplayenvironment="yes"
Runsinsnap="no"
;;
esac
Codename="$(unspecialstring "$Codename" | cut -c1-40)"
Codename="${Codename:-noname}"
Imagebasename="$(unspecialstring "$Imagebasename")" # must be - for backwards compatibility of --home
Imagebasename="${Imagebasename:-noname}"
return 0
}
check_terminalemulator() { # check terminal for password prompt of su or sudo
# $Passwordterminal: To prompt for su or sudo password
# Not working: pangoterm lilyterm fbterm
# Makes problems if X and Wayland are independently available at same time: xfce4-terminal
# Works, but does not appear: 'guake -te'
local Terminallist
Terminallist="xterm mintty lxterm lxterminal stterm sakura termit pterm terminator terminology Eterm konsole qterminal gnome-terminal mate-terminal mrxvt rxvt xvt kterm mlterm xfce4-terminal bash"
[ -z "$Hostdisplay" ] && [ -n "$Hostwaylandsocket" ] && Terminallist="konsole qterminal gnome-terminal bash"
[ "$Runsinterminal" = "yes" ] && Terminallist="bash"
for Passwordterminal in $Terminallist ; do command -v $Passwordterminal >/dev/null && break ; done
[ -z "$Hostdisplay" ] && [ -n "$Hostwaylandsocket" ] && {
case $Passwordterminal in
qterminal) Passwordterminal="env QT_QPA_PLATFORM=wayland $Passwordterminal -e" ;;
konsole) Passwordterminal="env QT_QPA_PLATFORM=wayland dbus-run-session $Passwordterminal --nofork -e" ;;
esac
}
[ -z "$Hostdisplay$Hostwaylandsocket" ] && Passwordterminal="bash"
case $Passwordterminal in
xfce4-terminal) Passwordterminal="$Passwordterminal --disable-server -x" ;;
mate-terminal) Passwordterminal="dbus-run-session $Passwordterminal -x" ;;
gnome-terminal) Passwordterminal="dbus-launch $Passwordterminal --" ;;
terminator) Passwordterminal="dbus-run-session $Passwordterminal --no-dbus -x" ;;
konsole) Passwordterminal="dbus-run-session $Passwordterminal --nofork -e" ;;
bash) Passwordterminal="eval" ;;
*) Passwordterminal="$Passwordterminal -e" ;;
esac
return 0
}
create_cachefiles() { # create empty cache files owned by unprivileged user
local Line
# create base cache folder
[ "$Cachebasefolder" ] || {
#Cachebasefolder="$Hostuserhome/.cache/x11docker" ### FIXME really a good idea for MS Windows? WSL cache provides performance, but maybe must not be shared with container to avoid file access errors.
case $Winsubsystem in
""|MSYS2|CYGWIN) Cachebasefolder="$Hostuserhome/.cache/x11docker" ;;
WSL1|WSL2)
case $Mobyvm in
yes)
Cachebasefolder="$(convertpath subsystem "$(wincmd "echo %userprofile%")")/x11docker/cache"
mkdir -p "$Hostuserhome/.cache/x11docker/symlink"
[ -e "$Hostuserhome/.cache/x11docker/symlink" ] || ln -s -T "$Cachebasefolder" "$Hostuserhome/.cache/x11docker/symlink"
mkfile "$Hostuserhome/.cache/x11docker/symlink/symlink.txt"
echo "x11docker: With MobyVM x11docker cache in WSL is stored in
$Cachebasefolder
to allow file sharing with containers.
A symbolic link is created in WSL at
$Hostuserhome/.cache/x11docker/symlink
" >> "$Hostuserhome/.cache/x11docker/symlink/symlink.txt"
;;
no)
Cachebasefolder="$Hostuserhome/.cache/x11docker"
;;
esac
;;
esac
}
[ "$Cachebasefolder" = "/x11docker/cache" ] && error "Failed to find a valid path for cache directory.
Please report at https://github.com/mviereck/x11docker
As a workaround you can specify a cache folder with --cachebasedir DIR."
Cachebasefolder="$(convertpath subsystem "$Cachebasefolder")"
[ "$Cachebasefolder" != "$(echo $Cachebasefolder | sed -e 's/ *//g')" ] && error "Cache root folder must not contain whitespaces.
$Cachebasefolder"
unpriv "mkdir -p $Cachebasefolder" || error "Could not create cache folder
$Cachebasefolder"
writeaccess $Hostuseruid $Cachebasefolder || error "User $Hostuser does not have write access to cache folder
$Cachebasefolder"
# Create cache subfolders
Cachefolder="$Cachebasefolder/$Codename-$Cachenumber"
[ -d "$Cachefolder" ] && error "Cache folder already exists:
$Cachefolder"
[ "$Cachefolder" != "$(escapestring "$Cachefolder")" ] && error "Invalid name created for cache folder:
$Cachefolder
Most probably provided image name (or --exe command) is invalid in some way:
$(escapestring "$Imagename")
For special setups like command chains use a syntax like:
x11docker IMAGENAME -- sh -c \"cd /etc && xterm\""
Sharefolder="$Cachefolder/$Sharefolder"
unpriv "mkdir -p $Sharefolder"
# Files in $Cachefolder: host only access
Compositorlogfile="$Cachefolder/$Compositorlogfile" && mkfile $Compositorlogfile
Dockercommandfile="$Cachefolder/$Dockercommandfile" && mkfile $Dockercommandfile
Dockerinfofile="$Cachefolder/$Dockerinfofile" && mkfile $Dockerinfofile
Dockerrc="$Cachefolder/$Dockerrc" && mkfile $Dockerrc
Dockerstopsignalfifo="$Cachefolder/$Dockerstopsignalfifo"
Hostxauthority="$Cachefolder/$Hostxauthority" && mkfile $Hostxauthority
Messagelogfile="$Cachefolder/$Messagelogfile" && mkfile $Messagelogfile
Nxagentclientrc="$Cachefolder/$Nxagentclientrc" && mkfile $Nxagentclientrc
Nxagentkeysfile="$Cachefolder/$Nxagentkeysfile" && mkfile $Nxagentkeysfile
Nxagentoptionsfile="$Cachefolder/$Nxagentoptionsfile" && mkfile $Nxagentoptionsfile
Pulseaudioconf="$Cachefolder/$Pulseaudioconf" && mkfile $Pulseaudioconf
Clipboardrc="$Cachefolder/$Clipboardrc" && mkfile $Clipboardrc
Storepidfile="$Cachefolder/$Storepidfile" && mkfile $Storepidfile
Systemdconsoleservice=$Cachefolder/$Systemdconsoleservice && mkfile $Systemdconsoleservice
Systemdenvironment=$Cachefolder/$Systemdenvironment && mkfile $Systemdenvironment
Systemdjournallogfile=$Sharefolder/$Systemdjournallogfile && mkfile $Systemdjournallogfile
Systemdjournalservice=$Cachefolder/$Systemdjournalservice && mkfile $Systemdjournalservice
Systemdtarget=$Cachefolder/$Systemdtarget && mkfile $Systemdtarget
Systemdwatchservice=$Cachefolder/$Systemdwatchservice && mkfile $Systemdwatchservice
Watchpidfifo="$Cachefolder/$Watchpidfifo"
Westonini="$Cachefolder/$Westonini" && mkfile $Westonini
Xdummyconf="$Cachefolder/$Xdummyconf" && mkfile $Xdummyconf
Xinitlogfile="$Cachefolder/$Xinitlogfile" && mkfile $Xinitlogfile
Xinitrc="$Cachefolder/$Xinitrc" && mkfile $Xinitrc
Xkbkeymapfile="$Cachefolder/$Xkbkeymapfile" && mkfile $Xkbkeymapfile
Xorgwrapper="$Cachefolder/$Xorgwrapper" && mkfile $Xorgwrapper
Xpraclientlogfile="$Cachefolder/$Xpraclientlogfile" && mkfile $Xpraclientlogfile
Xpraserverlogfile="$Cachefolder/$Xpraserverlogfile" && mkfile $Xpraserverlogfile
Xservercookie="$Cachefolder/$Xservercookie" && mkfile $Xservercookie
Xtermrc="$Cachefolder/$Xtermrc" && mkfile $Xtermrc
# Files in $Sharefolder: shared to /x11docker in container
Cmdrc="$Sharefolder/$Cmdrc" && mkfile $Cmdrc
Cmdstderrlogfile="$Sharefolder/$Cmdstderrlogfile" && mkfile $Cmdstderrlogfile 666
Cmdstdinfifo="$Sharefolder/$Cmdstdinfifo"
Cmdstdoutlogfile="$Sharefolder/$Cmdstdoutlogfile" && mkfile $Cmdstdoutlogfile 666
Containerrc="$Sharefolder/$Containerrc" && mkfile $Containerrc
Containerenvironmentfile="$Sharefolder/$Containerenvironmentfile" && mkfile $Containerenvironmentfile 666
Containerlocaltimefile="$Sharefolder/$Containerlocaltimefile"
Containerlogfile="$Sharefolder/$Containerlogfile" && mkfile $Containerlogfile 666
Containerrootrc="$Sharefolder/$Containerrootrc" && mkfile $Containerrootrc
Logfile="$Sharefolder/x11docker.log" && mkfile $Logfile 666
Messagefifo="$Sharefolder/$Messagefifo"
Pulseaudiocookie="$Sharefolder/$Pulseaudiocookie"
Pulseaudiosocket="$Sharefolder/$Pulseaudiosocket"
Storeinfofile="$Sharefolder/$Storeinfofile" && mkfile $Storeinfofile 666
Timetosaygoodbyefile="$Sharefolder/$Timetosaygoodbyefile" && mkfile $Timetosaygoodbyefile
Timetosaygoodbyefifo="$Sharefolder/$Timetosaygoodbyefifo"
Xclientcookie="$Sharefolder/$Xclientcookie" && mkfile $Xclientcookie
# Files in $Cachebasefolder
Dockerimagelistfile="$Cachebasefolder/$Dockerimagelistfile" && mkfile $Dockerimagelistfile
Logfilebackup="$Cachebasefolder/x11docker.log"
Modelinefile="$Cachebasefolder/$Modelinefile"
# file to store display numbers in use today
Numbersinusefile="$Cachebasefolder/$Numbersinusefile"
for Line in $(find $Cachebasefolder/displaynumbers.* 2>/dev/null ||:) ; do
[ "$Line" != "$Numbersinusefile" ] && rm "$Line"
done
[ -e "$Numbersinusefile" ] || mkfile "$Numbersinusefile"
# libc timezone file
[ -e "$Hostlocaltimefile" ] && cp "$Hostlocaltimefile" "$Containerlocaltimefile"
storeinfo "cache=$Cachefolder"
storeinfo "stdout=$Cmdstdoutlogfile"
storeinfo "stderr=$Cmdstderrlogfile"
return 0
}
drop_cachefiles() { # remove some cache files that are not needed in current setup
case $Initsystem in
systemd) ;;
*) rm $Systemdconsoleservice $Systemdenvironment $Systemdjournallogfile $Systemdjournalservice $Systemdtarget $Systemdwatchservice ;;
esac
case $Xserver in
--xpra|--xpra-xwayland) ;;
*) rm $Xpraclientlogfile $Xpraserverlogfile ;;
esac
case $Xserver in
--weston|--weston-xwayland|--kwin|--kwin-xwayland|--xpra-xwayland|--xdummy-xwayland) ;;
*) rm $Compositorlogfile $Westonini ;;
esac
case $Xserver in
--weston|--kwin|--hostwayland|--tty) rm $Xclientcookie $Xservercookie ;;
*) [ "$Xauthentication" = "no" ] && rm $Xclientcookie $Xservercookie ;;
esac
case $Xserver in
--nxagent) ;;
*) rm $Nxagentclientrc $Nxagentkeysfile $Nxagentoptionsfile ;;
esac
case $Xserver in
--xdummy) ;;
--xpra) [ "$Xpravfb" = "Xdummy" ] || rm $Xdummyconf $Xorgwrapper ;;
*) rm $Xdummyconf $Xorgwrapper ;;
esac
case $X11dockermode in
exe) rm $Containerrootrc $Dockercommandfile $Dockerinfofile $Dockerrc ;;
esac
case $Xserver in
--xephyr|--xorg|--xdummy|--xdummy-xwayland|--xvfb|--xwayland|--weston-xwayland)
[ "$Shareclipboard" = "no" ] && rm $Clipboardrc ;;
*) rm $Clipboardrc ;;
esac
case $Xserver in
--hostdisplay|--xwin|--nxagent|--hostwayland|--weston|--kwin|--tty) rm $Xkbkeymapfile ;;
esac
}
option_messages() { # some messages depending on options, but not changing settings
# X server specific messages
case $Xserver in
--hostdisplay)
[ "$Autochooseserver" = "yes" ] && [ -z "$Winsubsystem" ] && case "$Sharegpu" in
yes)
case $Nvidiaversion in
"") note "To allow protection against X security leaks,
please install 'xinit' and one or more of:
xpra, weston+Xwayland or kwin_wayland+Xwayland,
or run a second Xorg server with option --xorg." ;;
*) note "To allow protection against X security leaks
while using --gpu with NVIDIA, please use option --xorg." ;;
esac
;;
no) note "To allow protection against X security leaks,
please install 'xinit' and one or more of:
xpra, Xephyr, nxagent, weston+Xwayland, kwin_wayland+Xwayland or Xnest,
or run a second Xorg server with option --xorg." ;;
esac
case "$Trusted" in
no)
warning "Option --hostdisplay provides only low container isolation!
It is recommended to use another X server option like --nxagent or --xpra.
To improve security with --hostdisplay x11docker uses untrusted cookies.
This can lead to strange behaviour of some applications.
If you encounter application ${Colredbg}errors${Colnorm}, enable option --clipboard
that disables security restrictions for --hostdisplay as a side effect." ;;
yes)
case $Winsubsystem in
"") warning "Option --hostdisplay with trusted cookies provides
QUITE BAD CONTAINER ISOLATION !
Keylogging and controlling host applications is possible!
Clipboard sharing is enabled (option --cliboard).
It is recommended to use another X server option like --nxagent or --xpra." ;;
MSYS2) ;;
CYGWIN) warning "Option --hostdisplay allows less security hardening.
It is recommended to use option --xwin instead." ;;
WSL1|WSL2) warning "Option --hostdisplay allows less security hardening.
It is recommended to use another X server option like --nxagent or --xephyr." ;;
esac
;;
esac
[ "$Desktopmode" = "yes" ] && note "Can not avoid to use host window manager
along with option --hostdisplay.
You may get strange interferences with your host desktop.
Can be interesting though, having two overlapping desktops."
;;
--xorg)
[ "$Hostsystem" = "opensuse" ] && [ "$Runsonconsole" = "no" ] && [ "$Startuser" != "root" ] && warning "openSUSE does not support starting a second Xorg server
from within X. Possible solutions:
1.) Install nested X server 'Xephyr', 'nxagent' or 'Xnest',
or for --gpu support: install 'Weston' and 'Xwayland'.
2.) Switch to console tty1...tty6 with <CTRL><ALT><F1>...<F6>
and start x11docker there.
3.) Run x11docker as root."
case $Xlegacywrapper in
yes) warning "Although x11docker starts Xorg as unprivileged user,
most system setups wrap Xorg to give it root permissions (setuid).
Evil containers may try to abuse this.
Other x11docker X server options like --xephyr are more secure at this point." ;;
no) [ "$Startuser" = "root" ] && warning "x11docker will run Xorg as root." ;;
esac
[ "$Runsoverssh" = "yes" ] && warning "x11docker can run Xorg on another tty (option --xorg),
but you won't see it in your SSH session.
Rather install e.g. Xephyr on ssh server and use option --xephyr."
;;
--xpra|--xpra-xwayland)
verlt "$Xpraversion" "v1.0" && {
note "Your xpra version $Xpraversion is out of date. It is
recommended to install at least xpra v1.0. Look at: www.xpra.org"
[ "$Desktopmode" = "yes" ] && {
note "Your xpra version does not support desktop mode.
Please use another X server option like --xephyr or --nxagent."
} ||:
}
[ "$Desktopmode" = "yes" ] && verlt "$Xpraversion" "v2.2-r17117" && note "Xpra desktop mode works best since xpra v2.2-r17117.
You have installed lower version xpra $Xpraversion.
It is recommended to use --xephyr or --nxagent instead.
Rendering issues can be reduced disabling OpenGL in Xpra tray icon. Screen
size issues can be avoided with non-integer scaling (e.g. --scale=1.01)."
[ "$Desktopmode" = "no" ] && verlt $Xprarelease r23066 && note "Xpra startup can be slow. For faster startup
with seamless applications, try --nxagent.
If security is not a concern, try --hostdisplay.
Xpra version v3.0-r23066 and higher starts up faster."
[ "$Sharegpu" = "yes" ] && note "If performance of GPU acceleration with $Xserver
is not satisfying, you can try insecure '--hostdisplay --gpu'."
note "Option --xpra: If you encounter issues with xpra,
you can try --nxagent instead.
Rather use xpra from www.xpra.org than from distribution repositories."
;;
# --xephyr) note "Xephyr is a quite stable nested X server.
#Less stable, but directly resizeable is nxagent with option --nxagent.
#Resizing of the Xephyr window is possible with xrandr, arandr or lxrandr."
# ;;
--nxagent)
[ "$Hostsystem" = "mageia" ] && {
[ "$Desktopmode" = "no" ] && [ "$Autochooseserver" = "yes" ] && Desktopmode="yes" && Windowmanagermode="auto"
[ "$Desktopmode" = "no" ] && warning "nxagent version 3.5.0 on Mageia 6 is known to crash
in seamless mode. (Detected version: '$(strings --bytes 20 /usr/libexec/nx/nxagent | grep "NXAGENT - Version")').
If you encounter issues, please try seamless --xpra (secure),
--hostdisplay (insecure), or run --nxagent in desktop mode with a
host window manager (--wm=WINDOWMANAGER or --wm=auto or short -wm)."
}
note "A few applications do not work well with --nxagent.
In that case, please try another X server option like --xephyr or --xpra."
;;
--weston|--kwin|--hostwayland)
note "You are running a pure Wayland environment.
X applications without Wayland support will fail."
[ "$Xserver" = "--kwin" ] && note "kwin_wayland (option --kwin) does not support the xdg_shell
interface in all versions. Some GTK3 Wayland applications depend on it.
If application startup fails, try --weston instead."
;;
esac
# NVIDIA without --gpu
[ "$Nvidiaversion" ] && [ "$Sharegpu" = "no" ] && case $Xserver in
--hostdisplay|--xorg) note "Option $Xserver may fail with proprietary NVIDIA driver
on host. In that case try other X server options like
--nxagent, --xpra or --xephyr." ;;
esac
# --fullscreen
[ "$Fullscreen" = "yes" ] && {
case $Xserver in
--xephyr|--weston|--weston-xwayland|--nxagent|--xpra|--xpra-xwayland|--xwin) ;;
--xdummy|--xdummy-xwayland|--xvfb|--xorg) ;;
*) note "$Xserver does not support option --fullscreen" ;;
esac
}
# --output-count
[ "$Outputcount" != "1" ] && {
case $Xserver in
--weston-xwayland) note "Xwayland sometimes does not position itself well
at origin 0+0 of first virtual screen, and some screens appear to be unused.
You may need to move Xwayland manually with [META]+[LeftMouseButton].
(Bug report at https://bugzilla.redhat.com/show_bug.cgi?id=1498665 )" ;;
--xephyr) note "Xinerama support would be best for multiple outputs,
but is disabled in Xephyr because Xephyr does not handle it well.
Different window managers handle this different. Just try out." ;;
esac
}
# --hostipc
[ "$Sharehostipc" = "yes" ] && warning "Option --hostipc severely degrades
container isolation. IPC namespace remapping is disabled."
# --network
[ "$Network" = "host" ] && warning "Option --network=host severly degrades
container isolation. Network namespacing is disabled.
Container shares host network stack.
Spying on network traffic may be possible.
Access to host X server $Hostdisplay may be possible
through abstract unix socket."
# --webcam
[ "$Sharewebcam" = "yes" ] && warning "Option --webcam: Container applications might look
at you and also might take screenshots of your Desktop."
# --user=RETAIN / keep container defined in image
case $Createcontaineruser in
no)
[ "$Sudouser" = "yes" ] && note "Option --sudouser has limited support with --user=RETAIN.
x11docker will only set needed capabilities.
User setup and /etc/sudoers won't be touched.
Option --group-add=sudo might be useful."
;;
esac
[ "$Sudouser" = "yes" ] && [ "$Containeruseruid" != "0" ] && [ "$Network" != "host" ] && [ -z "$Xoverip" ] && note "Option --sudouser: If you want to run GUI application
with su or sudo, you might need to add either option --xoverip
or (discouraged) option --network=host."
[ "$Customdockeroptions" ] && {
warning "Found custom DOCKER_RUN_OPTIONS.
x11docker will add them to 'docker run' command without
a serious check for validity or security. Found options:
$Customdockeroptions"
grep -q -- '--privileged' <<< "$Customdockeroptions" && warning "Found option --privileged
in custom docker run options. That is A VERY BAD IDEA.
A privileged setup allows unrestriced access from container to host.
Malicious applications can cause arbitrary harm."
grep -q -i -- '--cap-add.ALL' <<< "$Customdockeroptions" && warning "Found option --cap-add=ALL
in custom docker run options. That is A VERY BAD IDEA.
That is a very privileged setup.
Malicious applications may harm to the host."
grep -q -i -- '--cap-add.SYS_ADMIN' <<< "$Customdockeroptions" && warning "Found option --cap-add=SYS_ADMIN
in custom docker run options. That is A VERY BAD IDEA.
That is a very privileged setup.
Malicious applications may harm to the host."
grep -q -- '--entrypoint' <<< "$Customdockeroptions" && warning "Found option --entrypoint
in custom docker run options. x11docker uses this option, too.
This setup will probably fail. Use x11docker option --no-entrypoint instead
and add desired command as container command after the image name."
grep -q -- "--user" <<< "$Customdockeroptions" && warning "Found option --user in custom DOCKER_RUN_OPTIONS.
This might lead to errors or unexpected behaviour.
Please use x11docker option --user instead."
grep -q -- "--runtime" <<< "$Customdockeroptions" && note "Found option --runtime in custom DOCKER_RUN_OPTIONS.
Please use x11docker option --runtime instead."
grep -q -- "--network" <<< "$Customdockeroptions" && note "Found option --network in custom DOCKER_RUN_OPTIONS.
Please use x11docker option --network instead."
grep -q -- "--name" <<< "$Customdockeroptions" && note "Found option --name in custom DOCKER_RUN_OPTIONS.
Please use x11docker option --name instead."
grep -q -- "--group-add" <<< "$Customdockeroptions" && note "Found option --group-add in custom DOCKER_RUN_OPTIONS.
Please use x11docker option --group-add instead."
}
return 0
}
setup_fifo() { # set up fifo channels (also option --stdin)
# setup fifos to allow messages from within container, dockerrc and xinitrc
# and to send pids to watch to watchpidlist() thread
# file descriptors in use:
# FDstderr stderr for warnings and notes redirected to &2, with --silent redirected to /dev/null
# FDmessage $Messagefifo for messages from other threads to watchmessagefifo()
# FDcmdstdin stdin>>$Cmdstinfile --stdin with catstdin, redirection of &0
# FDtimetosaygoodbye $Timetosaygoodbyefifo for saygoodbye() and waitfortheend()
# FDwatchpid $Watchpidfifo for watchpidlist()
# FDdockerstop $Dockerstopsignalfifo to send docker stop singnal to dockerrc in finish()
case "$Mobyvm" in
yes) Usemkfifo="no" ;;
no) Usemkfifo="yes" ;;
esac
[ "$Runtime" = "kata-runtime" ] && Usemkfifo="no"
# redirect stdin to named pipe. Named pipe is shared with container and used as stdin of container command in containerrc
[ "$Forwardstdin" = "yes" ] && {
case $Usemkfifo in
yes) unpriv "mkfifo $Cmdstdinfifo" ;;
no) mkfile $Cmdstdinfifo ;;
esac
exec {FDcmdstdin}<>$Cmdstdinfifo
cat <&0 >${FDcmdstdin} & storepid $! catstdin
storeinfo "stdin=$Cmdstdinfifo"
}
case $Usemkfifo in
yes)
unpriv "mkfifo $Watchpidfifo"
unpriv "mkfifo $Messagefifo && chmod 666 $Messagefifo"
unpriv "mkfifo $Timetosaygoodbyefifo"
;;
no) # Windows, kata
mkfile $Watchpidfifo
mkfile $Messagefifo 666
mkfile $Timetosaygoodbyefifo 666
;;
esac
case $Mobyvm in
no)
# used by finish() and dockerrc
unpriv "mkfifo $Dockerstopsignalfifo"
exec {FDdockerstop}<>$Dockerstopsignalfifo
;;
esac
# used by waitfortheend()
exec {FDtimetosaygoodbye}<>$Timetosaygoodbyefifo
# start watching important pids, e.g. xinit, container.
exec {FDwatchpid}<>$Watchpidfifo
watchpidlist & storepid $! watchpidlist
# start watching for messages out of container or dockerrc
exec {FDmessage}<>$Messagefifo
watchmessagefifo & storepid $! watchmessagefifo
return 0
}
setup_verbosity() { # options --verbose, --stdout, --stderr
local Line Logfiles
# create summary logfile
Logfiles="
$Cmdstderrlogfile
$Cmdstdoutlogfile
$Compositorlogfile
$Containerlogfile
$Systemdjournallogfile
$Messagelogfile
$Xinitlogfile
$Xpraclientlogfile
$Xpraserverlogfile
"
for Line in $Logfiles; do
[ -e "$Line" ] && grep -q "/" <<< "$Line" && Logfiles="$Logfiles $Line"
done
{
trap '' SIGINT
tail --pid="$$" --retry -n +1 -F $Logfiles 2>/dev/null >>$Logfile ||:
} &
# option --verbose
[ "$Verbose" = "yes" ] && {
trap '' SIGINT
case $Verbosecolors in
no) tail --pid="$$" --retry -n +1 -F $Logfile 2>/dev/null >&${FDstderr} ;;
yes) tail --pid="$$" --retry -n +1 -F $Logfile 2>/dev/null | sed "
/\(Failed to add fd to store\|Failed to set invocation ID\|Failed to reset devices.list\)/d;
s/\(ERROR\|Error\|error\|FAILURE\|FATAL\|Fatal\|fatal\)/${Colredbg}\1${Colnorm}/g;
s/\(Failed\|failed\|Failure\|failure\)/${Colred}\1${Colnorm}/g;
s/\(WARNING\|Warning\|warning\)/${Colyellow}\1${Colnorm}/g;
s/\(DEBUGNOTE\)/${Colblue}\1${Colnorm}/g;
s/^==>.*/${Coluline}\0${Colnorm}/;
s/\(Starting\|Activating\)/${Colgreen}\0${Colnorm}/;
s/\(Started\|Reached target\|activated\)/${Colgreenbg}\0${Colnorm}/;
s/^\(+\|++\|+++\)/${Colgreenbg}\0${Colnorm}/ ;
s/^x11docker/${Colgreen}\0${Colnorm}/ " >&${FDstderr}
;;
esac
} &
[ "$Showcontaineroutput" = "yes" ] && {
{
waitforlogentry tailstdout "$Storeinfofile" "x11docker=ready" "" infinity ||:
trap '' SIGINT
tail --pid="$$" -n +1 -f $Cmdstdoutlogfile 2>/dev/null ||:
} &
{
waitforlogentry tailstderr "$Storeinfofile" "x11docker=ready" "" infinity ||:
trap '' SIGINT
tail --pid="$$" -n +1 -f $Cmdstderrlogfile >&2 2>/dev/null ||:
} &
}
return 0
}
#### main
declare_variables() { # declare global variables
export IFS=$' \n\t' # set IFS to default
# Global environment variables used in x11docker
ALSA_CARD=$(check_envvar "${ALSA_CARD:-}")
CUPS_SERVER=$(check_envvar "${CUPS_SERVER:-}")
DBUS_SESSION_BUS_ADDRESS=$(check_envvar "${DBUS_SESSION_BUS_ADDRESS:-}")
DISPLAY=$(check_envvar "${DISPLAY:-}")
GDK_BACKEND=$(check_envvar "${GDK_BACKEND:-}")
HOME=$(check_envvar "${HOME:-}")
LANG=$(check_envvar "${LANG:-}")
LC_ALL=$(check_envvar "${LC_ALL:-}")
PATH="$(check_envvar -w "${PATH:-}")"
WAYLAND_DISPLAY=$(check_envvar "${WAYLAND_DISPLAY:-}")
XAUTHORITY=$(check_envvar "${XAUTHORITY:-}")
XDG_CURRENT_DESKTOP=$(check_envvar "${XDG_CURRENT_DESKTOP:-}")
XDG_RUNTIME_DIR=$(check_envvar "${XDG_RUNTIME_DIR:-}")
XDG_VTNR=$(check_envvar "${XDG_VTNR:-}")
# Add possibly missing PATH entries
PATH="${PATH:-"/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/games:/usr/games"}"
grep -q ':/sbin:' <<< ":$PATH:" || PATH="$PATH:/sbin"
grep -q ':/usr/sbin:' <<< ":$PATH:" || PATH="$PATH:/usr/sbin"
grep -q ':/usr/local/bin:' <<< ":$PATH:" || PATH="$PATH:/usr/local/bin"
grep -q ':/usr/bin:' <<< ":$PATH:" || PATH="$PATH:/usr/bin"
grep -q ':/usr/local/games:' <<< ":$PATH:" || PATH="$PATH:/usr/local/games"
grep -q ':/usr/games:' <<< ":$PATH:" || PATH="$PATH:/usr/games"
export PATH
# File descriptors
FDcmdstdin="" # --stdin channel to forward stdin to container. Previously &7
FDdockerstop="" # message channel to send docker stop signal to dockerrc. Previously &4
FDmessage="" # message channel for notes, warnings and verbosity across threads and container. Previously &6
#FDstderr="" # internal stderr >&2, redirected to null with --silent. Alread declared in main(). Previously &3
FDtimetosaygoodbye="" # message channel to send termination signal from or to containers. Previously &8
FDwatchpid="" # message channel for watchpidlist(). Previously &9
# Terminal colors used for messages and -V
Esc="$(printf '\033')"
Colblue="${Esc}[35m"
Colyellow="${Esc}[33m"
Colgreen="${Esc}[32m"
Colgreenbg="${Esc}[42m"
Colred="${Esc}[31m"
Colredbg="${Esc}[41m"
Coluline="${Esc}[4m"
Colnorm="${Esc}[0m"
# x11docker startup environment
Runsinsnap="" # docker runs in Ubuntu snap yes/no
Runsinteractive="" # --enforce-i: Script runs in bash interactive mode (bash -i) yes/no.
Runsinterminal="" # x11docker runs in a terminal yes/no
Runsonconsole="" # x11docker runs on tty yes/no
Runsoverssh="" # x11docker runs over SSH yes/no. Makes a difference for --hostdisplay
Runssourced="" # x11docker has been sourced yes/no
# Generated scripts
Clipboardrc="clipboardrc" # --clipboard: Generated script for text clipboard sharing
Cmdrc="cmdrc" # Generated script starting container command
Containerrc="containerrc" # Generated script starting cmdrc
Containerrootrc="containerrootrc" # Generated script to set up container, e.g. user creation. Runs as root in container.
Dockerrc="dockerrc" # Generated script to check image, set up host and create $Containerrc
Xinitrc="xinitrc" # Generated script to set up X, e.g. cookie and xrandr
Xtermrc="xtermrc" # Generated script for password prompt
# Internal messages
Dockerstopsignalfifo="$Dockerrc.stopfifo" # finish() send 'docker stop' signal to dockerrc
Logmessages="" # Stores messages until logfile is available, needed by logentry()
Messagefifo="message.fifo" # Message channel for warning/verbose/debugnote/note/error within container, dockerrc, containerrootrc and others
Storeinfofile="store.info" # File to store some info like id, pid, name, exit code
Storepidfile="store.pids" # File to store pids and names of background processes that should be terminated on exit
Timetosaygoodbyefile="timetosaygoodbye" # File giving term signal to all parties
Timetosaygoodbyefifo="timetosaygoodbye.fifo" # Message channel for --init=openrc|runit|sysvinit to shut down on x11docker signal
Usemkfifo="" # Not on Windows nor with kata-runtime
Watchpidfifo="watchpid.fifo" # Message channel to transfer pids to watchpidlist()
X11dockererrorfile="x11docker.error" # Store error exit code
# Logfiles
Compositorlogfile="compositor.log" # Logfile for weston or kwin_wayland
Containerlogfile="container.log" # Logfile for container output other than container command output
Logfile="" # $Cachefolder/x11docker.log (current log)
Logfilebackup="" # $Cachebasefolder/x11docker.log (latest terminated log)
Messagelogfile="message.log" # Logfile for warning/verbose/debugnote/note/error
Xinitlogfile="xinit.log" # Logfile for xinit/X server
Xpraclientlogfile="xpra.client.log" # Logfile for xpra client
Xpraserverlogfile="xpra.server.log" # Logfile for xpra server
Dockercommandfile="docker.command" # File to store generated docker command, needed for --interactive
Dockerimagelistfile="docker.imagelist" # File with list of local images.Used by x11docker-gui, too
Dockerinfofile="docker.info" # File to store outpu of 'docker info'
# Generated commands
Compositorcommand="" # Command to start Weston or KWin
Dockercommand="" # Command to run docker
Wmdockercommand="" # --wm: Command to run x11docker/openbox
Xcommand="" # Command to start X server
Xpraclientcommand="" # xpra client command
Xpraservercommand="" # xpra server command
# Users
Hostuser="" # $Lognameuser or --hostuser. Unprivileged user for non-root commands. Compare unpriv()
Hostusergid=""
Hostuserhome=""
Hostuseruid=""
Containeruser="" # --user: Container user. Default: same as $Hostuser.
Containeruseruid=""
Containerusergid=""
Containerusergroup=""
Containerusergroups="" # --group-add: Additional groups for container user
Containeruserhome="" # HOME path within container
Containeruserhosthome="" # HOME path of container user on host
Containeruserpassword="sac19FwGGTx/A" # Encrypted password "x11docker", suits /etc/shadow. Generated with: perl -e 'print crypt("x11docker", "salt"),"\n"'
Createcontaineruser="yes" # exception: --user=RETAIN
Lognameuser="" # $(logname) or $SUDO_USER or $PKEXEC_USER
Persistanthomevolume="" # --home: Path to shared host folder or docker volume used as HOME in container.
Startuser="" # User who started x11docker
Unpriv="" # Command to run commands as unprivileged user
Containerusershell="auto" # --shell: Preferred user shell
# Hostsystem
Hostarchitecture="" # uname -m, checked
Hostcanwatchroot="" # x11docker can watch root processes yes/no. Related to $Hosthidepid
Hostdisplay="" # Environment variable DISPLAY
Hostdisplaynumber="" # DISPLAY without : (and without possible IP)
Hosthidepid="" # /proc is mounted with hidepid=2 yes/no. Seen on NixOS.
Hostip="" # An IP address to access host. Preferred: IP of docker daemon
Hostlibc="" # glibc or musl. Can be important for locale and timezone.
Hostlocaltimefile="" # Time zone from host, myrealpath /etc/localtime
Hostmitshm="yes" # X on host has extension MIT-SHM enabled yes/no. Assume yes, check later
Hostsystem="" # $ID from /etc/os-release
Hostutctime="" # Time zone from host as offset to UTC
Hostwaylandsocket="$WAYLAND_DISPLAY" # Store host wayland socket name
Hostxauthority="Xauthority.host.$(unspecialstring "${DISPLAY:-unknown}")" # File to store copy of $XAUTHORITY
Hostxenv="" # Collection of host X environment variables
Hostxsocket="" # Socket of DISPLAY in /tmp/.X11-unix
Nvidiacontainerfile="/usr/local/bin/NVIDIA-installer.run" # --gpu: Path to nvidia installer in container
Nvidiaversion="" # --gpu: Proprietary nvidia driver version on host
Nvidiainstallerfile="" # --gpu: Proprietary nvidia driver installer for container in [...]local/share/x11docker
# MS Windows
Winpty="" # Path to winpty for --interactive on MS Windows
Winsubmount="" # Path within subsystem to mounted MS Windows drives
Winsubpath="" # Path within MS Windows to subsystem files
Winsubsystem="" # MS Windows subsystem WSL1, WSL2, MSYS2 or CYGWIN
Mobyvm="" # MS Windows: Use MobyVM yes/no (No only for WSL2 possible)
# Cache folders
Cachebasefolder="" # --cachebasedir Base cache folder
Cachefolder="" # Subfolder of $Cachebasefolder for current container
Sharefolder="share" # Subfolder of $Cachefolder for cache files shared with container
Sharefoldercontainer="/x11docker" # Mountpoint of $Sharefolder in container
# stdin stdout stderr
Cmdstdinfifo="stdin" # stdin for container command. fifo/named pipe to forward stdin of x11docker to container command
Cmdstderrlogfile="stderr" # stderr for container command
Cmdstdoutlogfile="stdout" # stdout for container command
Forwardstdin="no" # --stdin: forward stdin to container command yes/no
# X and Wayland configuration
Autochooseserver="yes" # --auto: automatical choice of X server (default)
Cleanxhost="no" # --clean-xhost: remove xhost access policies on host X
Compositorerrorcodes="Failed to process Wayland|failed to create display|] fatal:"
Desktopmode="no" # --desktop: image contains a desktop enironment.
Dpi="" # --dpi: dots per inch. Influenxes font size
Fullscreen="no" # --fullscreen: Fullscreen mode
Iglx="" # --iglx: Enable indirect rendering yes/no/""
Lastcheckedxserver="" # check_xdepends(): Last X server option that was checked
Lastcheckedxserverresult="" # check_xdepends(): Result of last check. Avoids double-checking.
Maxxaxis="" # Maximal X screen size of display
Maxyaxis="" # Maximal Y screen size of display
Modelinefile="modelines"
Newdisplay="" # --display: New DISPLAY for new X server
Newdisplaynumber="" # --display: New display number for new X server.
Newwaylandsocket="" # Wayland socket of $Compositorcommand
Newxenv="" # Environment variables for new X server: DISPLAY XAUTHORITY XSOCKET WAYLAND_DISPLAY
Newxlock="" # .Xn-lock - exists for running X server with socket n
Newxsocket="" # New X unix socket
Newxvt="" # --vt: number of virtual console to use for --xorg
Numbersinusefile="displaynumbers.$(date +%y_%m_%d)" # File to store display numbers used today. Helps to avoid race conditions on simultuanous startups
Nxagentclientrc="nxagent.nxclientrc" # --nxagent NX_CLIENT script to catch nxagent messages
Nxagentkeysfile="nxagent.keys" # --nxagent keyboard shortcut config
Nxagentoptionsfile="nxagent.options" # --nxagent options not available on cli, but possible in config file
Modeline="" # Screen modeline describing display size, see "man cvt". Needed for Xdummy
Outputcount="1" # --output-count: quantum of virtual screens for Weston or Xephyr
Rotation="" # --rotate: Rotation for --weston, --weston-xwayland or --xorg: 0/90/180/270/flipped/flipped-90/..
Scaling="" # --scale: Scaling factor for xpra and weston
Screensize="" # --size XxY: Display size
Setupwayland="no" # --wayland, --kwin, --weston --hostwayland: Provide a Wayland environment
Trusted="yes" # Create trusted or untrusted cookies, --hostdisplay uses untrusted cookies by default
Waylandtoolkitenv="XDG_SESSION_TYPE=wayland GDK_BACKEND=wayland QT_QPA_PLATFORM=wayland CLUTTER_BACKEND=wayland SDL_VIDEODRIVER=wayland ELM_DISPLAY=wl ELM_ACCEL=opengl ECORE_EVAS_ENGINE=wayland_egl"
Waylandtoolkitvars="XDG_SESSION_TYPE GDK_BACKEND QT_QPA_PLATFORM CLUTTER_BACKEND SDL_VIDEODRIVER ELM_DISPLAY ELM_ACCEL ECORE_EVAS_ENGINE"
Xauthentication="yes" # --no-auth: use cookie authentication and disable xhost yes/no
Xaxis="" # Virtual screen width
Xcomposite="" # --xcomposite: +extension COMPOSITE yes/no
Xkblayout="" # --keymap: Layout for keymap, compare /usr/share/X11/xkb/symbols
Xfishtank="no" # --xfishtank: Show a fish tank on new X server
Xhost="" # --xhost: custom xhost setting on new X server
Xiniterrorcodes="xinit: giving up|unable to connect to X server|Connection refused|server error|Only console users are allowed"
Xlegacywrapper="" # --xorg: /etc/X11/Xwrapper.config is configured to run within X yes/no
Xpraborder="" # --border: Colored border for xpra clients
Xpracontainerenv="UBUNTU_MENUPROXY= QT_X11_NO_NATIVE_MENUBAR=1 MWNOCAPTURE=true MWNO_RIT=true MWWM=allwm GTK_OVERLAY_SCROLLING=0 GTK_CSD=0 NO_AT_BRIDGE=1" # environment variables
Xprahelp="" # Output of 'xpra --help'
Xprarelease="" # Release number from $Xpraversion
Xprashm="" # Content XPRA_XSHM=0 disables usage of MIT-SHM in xpra
Xpraversion="" # $(xpra --version) to decide some xpra options and messages
Xpravfb="" # --xpra, --xdummy, --xvfb: vfb for --xpra: Xdummy or Xvfb
Xserver="" # X server option to use
Xoverip="" # --xoverip: Connect to X over TCP yes/no
Xserveroptions="" # --xopt: Custom X server options
Xtest="" # --xtest: Enable extension Xtest yes/no. If empty, yes for --xpra/--xdummy/--xvfb, otherwise no
Yaxis="" # Virtual screen height
# X and Wayland config and cookie files
Customwestonini="" # --westonini: Custom config file for weston
Westonini="weston.ini" # Generated config file for weston
Xdummyconf="xorg.xdummy.conf" # --xdummy, --xpra: Generated xorg.conf for dummy video driver
Xclientcookie="Xauthority.client" # Generated X client cookie. Normally same as $Xservercookie, except for --hostdisplay and --nxagent
Xkbkeymapfile="xkb.keymap" # --keymap: File to store output of host keymap in xinitrc
Xorgconf="" # --xorgconf: custom xorg.conf
Xservercookie="Xauthority.server" # Generated X server cookie
Xorgwrapper="Xorg.xdummy.wrapper" # --xpra, --xdummy: Fork from xpra to wrap Xorg for Xdummy
# Window manager
Windowmanagermode="none" # --wm: Window manager to use: container/host/auto
Windowmanagercommand="" # --wm: Argument for --wm, host command or docker image
Hostwindowmanager="" # --wm: A window manager from host, given or autodetected
# Host integration
Alsacard="$ALSA_CARD" # --alsa: Specified ALSA card
Hosthomebasefolder="" # --homebasedir: Base directory for container home with --home
Langwunsch="" # --lang: Search or create UTF-8 locale in container and set LANG
Pulseaudioconf="pulseaudio.client.conf" # --pulseaudio: Client config in container
Pulseaudiocookie="pulseaudio.cookie" # --pulseaudio: possible pulse cookie from host to share
Pulseaudiomode="" # --pulseaudio: 'tcp', 'socket' or 'auto'
Pulseaudiomoduleid="" # --pulseaudio: module ID, stored for unload in finish()
Pulseaudioport="" # --pulseaudio: TCP port for --pulseaudio=tcp
Pulseaudiosocket="pulseaudio.socket" # --pulseaudio: unix socket for --pulseaudio=socket
Sharealsa="no" # --alsa: enable ALSA sound, share /dev/snd
Shareclipboard="no" # --clipboard: Enable clipboard sharing
Sharecupsmode="" # --printer: Share access to CUPS printer server: socket|tcp|""
Sharegpu="no" # --gpu: Use hardware accelerated OpenGL, share files in /dev/dri
Sharehome="no" # --home: Share a folder ~/.local/share/x11docker/Imagename with created container
Sharevolumes="" # --share: Host files or folders or devices to share, array
Sharevolumescount="0" # --share: Counts shared folders in array
Sharewebcam="no" # --webcam: Share webcam device /dev/video*
# Container setup
Adminusercaps="no" # --cap-default, --sudouser, --user=root, --init=systemd: add capabilities for general container system administration
Allownewprivileges="auto" # --newprivileges: Docker run option --security-opt=no-new-privileges. Default: no. Enabled by options --newprivileges, --cap-default, --sudouser and --user=root.
Capabilities="" # Capabilities to add. Default: none, exceptions for --init, --sudouser
Capdropall="yes" # --cap-default: Drop all container capabilities and set --securty-opt=no-new-privileges yes/no
Containercommand="" # Container command [+args]
Containerenvironment="" # --env: Environment variables
Containerenvironmentcount="0"
Containerenvironmentfile="container.environment" # file to store final container environment
Containerlocaltimefile="libc.localtime" # localtime file from host shared to container
Containername="" # --name: Container name
Containersetup="yes"
Customdockeroptions="" # -- [...] -- : Custom options for "docker run".
Imagename="" # Image to run
Interactive="no" # --interactive: Run docker with interactive tty yes/no
Limitresources="" # --limit: Limit access to CPU and RAM, 0.1 ... 1.0
Network="" # --network
Noentrypoint="no" # --no-entrypoint: Disable entrypoint in image yes/no
Podman="no" # --podman: Use podman instead of docker
Runtime="" # Runtime to use. runc|nvidia|kata-runtime|crun
Sharehostipc="no" # --hostipc: Set --ipc=host.
Stopsignal="" # Signal to send on 'docker stop'
Sudouser="no" # --sudouser: Create user with sudo permissions and root user with password 'x11docker'
Switchcontaineruser="no" # --init=systemd|openrc|runit|sysvinit: User switching to trigger login services yes/no
Switchcontainerusercaps="no" # --init=systemd|openrc|runit|sysvinit, --sudouser, --user=root: Add capabilities for su/sudo user switching
Systemdconsoleservice="systemd.console-getty.service" # --init=systemd
Systemdenvironment="systemd.environment.conf"
Systemdjournallogfile="systemd.journal.log"
Systemdjournalservice="systemd.journal.service"
Systemdtarget="systemd.x11docker.target"
Systemdwatchservice="systemd.watch.service"
Workdir="" # --workdir: Set working directory in container
# Init and DBus
Dbusrunsession="no" # --dbus, --wayland, --init=systemd|openrc|runit|sysvinit: Run container command with dbus-run-session / DBus user session
Dbussystem="no" # --init=systemd|openrc|runit|sysvinit: Run DBus system daemon in container
Initsystem="tini" # --init: Init system in container
Sharecgroup="no" # --sharecgroup, --init=systemd: share /sys/fs/cgroup. Also needed for elogind
Sharehostdbus="no" # --hostdbus: Connect to DBus user daemon on host
Tinibinaryfile="" # --init=tini (default): Binary of tini; either /usr/bin/docker-exec or provided by user in [...]/share/x11docker
Tinicontainerpath="/usr/local/bin/init" # --init=tini: Path of tini (or catatonit) in container
# Gaining root privileges to run docker
Passwordcommand="" # --pw: Generated command for password prompt
Passwordfrontend="" # --pw: Frontend for password. One of pkexec, su, sudo, gksu, gksudo, kdesu, kdesudo, lxsu, lxsudo, beesu, auto, none
Passwordneeded="yes" # Password needed to run docker? assume yes, check later
Sudo="" # "sudo", "sudo -n", or empty. Added as prefix to some privileged commands, especially docker.
# Custom additional commands
Runasuser="" # --runasuser: Add container command to containerrc
Runasroot="" # --runasroot: Add container command to container setup script running as root
Runfromhost="" # --runfromhost: Add host command to xinitrc
# Miscellanous
Cachenumber="$(date +%s%N | cut -c6-16)" # Number to use for cache folder
[ -z "$Cachenumber" ] && Cachenumber="$(makecookie)"
Codename="" # created from image name and command without special chars for use with container name and cache folder
Dockerexe="" # Can be docker.exe on MS Windows
Fallback="yes" # --fallback: Allow or deny fallbacks for failing options.
Hostexe="" # --exe: Host command
Imagebasename="" # Image name without tags and / replaced with -. For use of --home folders.
Parsedoptions_global="" # Parsed options
Passwordterminal="" # Terminal emulator to use for password prompt (if no terminal emulator is needed, it will be 'bash -c')
Presetdirlocal="$HOME/.config/x11docker/preset"
Presetdirsystem="/etc/x11docker/preset"
Preservecachefiles="no" # If yes, don't delete cache files on exit. For few failure cases only.
Pullimage="ask" # --pull: Allow 'docker pull' yes|no|always|ask
X11dockermode="run" # --exe, --xonly: Can be either "run" (default), "exe", or "xonly".
# Verbosity options
Debugmode="no" # --debug: Excerpt of --verbose, also bash error checks
Showcache="no" # --showcache: Output of $Cachefolder on stdout (x11docker-gui only)
Showcontainerid="no" # --showid: Output of container ID on stdout
Showcontaineroutput="yes" # Show container command stdout
Showcontainerpid1pid="no" # --showpid1: Output of host PID of container PID 1 on stdout
Showdisplayenvironment="no" # --showenv: Output of environment variables of new display on stdout
Showinfofile="no" # --showinfofile: Show path of $Storeinfofile
Silent="no" # --quiet: Do not show x11docker messages
Verbose="no" # --verbose: Be verbose yes/no
Verbosecolors="no" # -V: colored output for --verbose (and delete some noisy systemd error messages)
Wikipackages="You can look for the package name of this command at:
https://github.com/mviereck/x11docker/wiki/dependencies#table-of-all-packages"
# Special options not starting X or docker
Cleanup="no" # --cleanup: Remove orphaned containers and cache files
Createlauncher="no" # --launcher: Create application launcher on desktop and exit yes/no
Installermode="" # --install/--update/--update-master/--remove
# Lists of window managers
# - these window managers are known to work well with x11docker (alphabetical order)(excluding $Wm_not_recommended and $Wm_ugly):
Wm_good="amiwm blackbox cinnamon compiz ctwm enlightenment fluxbox flwm fvwm"
Wm_good="$Wm_good jwm kwin kwin_x11 lxsession mate-session mate-wm marco metacity notion olwm olvwm openbox ororobus pekwm"
Wm_good="$Wm_good sawfish twm wmaker w9wm xfwm4"
# - these wm's are recommended and lightweight, but cannot show desktop options. best first:
Wm_recommended_nodesktop_light="xfwm4 metacity marco openbox sawfish"
# - these wm's are recommended and heavy, but cannot show desktop options (especially exiting themselves). best first:
Wm_recommended_nodesktop_heavy="kwin compiz"
# - these wm's are recommended, lightweight AND desktop independent. best first:
Wm_recommended_desktop_light="flwm blackbox fluxbox jwm mwm wmaker afterstep amiwm fvwm ctwm pekwm olwm olvwm openbox"
# - these wm's are recommended, heavy AND desktop independent. best first:
Wm_recommended_desktop_heavy="lxsession mate-session enlightenment cinnamon cinnamon-session plasmashell"
# - these wm's are not really useful (please don't hit me) (best first):
Wm_not_recommended="awesome evilwm herbstluftwm i3 lwm matchbox miwm mutter spectrwm subtle windowlab wmii wm2"
# - these wm's cannot be autodetected by wmctrl if they are already running
Wm_nodetect="aewm aewm++ afterstep awesome ctwm mwm miwm olwm olvwm sapphire windowlab wm2 w9wm"
# - these wm's can cause problems (they can be beautiful, though):
Wm_ugly="icewm sapphire aewm aewm++"
# - these wm's doesn't work:
Wm_bad="budgie-wm clfswm tinywm tritium muffin gnome-shell"
# List of all working window managers, recommended ones first, excluding $Wm_bad:
Wm_all="$Wm_recommended_nodesktop_light $Wm_recommended_nodesktop_heavy $Wm_recommended_desktop_light $Wm_recommended_desktop_heavy $Wm_good $Wm_ugly $Wm_not_recommended $Wm_nodetect"
# x11docker communication functions to integrate into generated scripts
Messagefifofuncs='
warning() {
echo "$*:WARNING" | sed "s/\$/ /" >>$Messagefile
}
note() {
echo "$*:NOTE" | sed "s/\$/ /" >>$Messagefile
}
verbose() {
echo "$*:VERBOSE" | sed "s/\$/ /" >>$Messagefile
}
debugnote() {
echo "$*:DEBUGNOTE" | sed "s/\$/ /" >>$Messagefile
}
error() {
echo "$*:ERROR" | sed "s/\$/ /" >>$Messagefile
exit 64
}
stdout() {
echo "$*:STDOUT" | sed "s/\$/ /" >>$Messagefile
}'
Messagefifofuncs_escaped='
warning() {
echo \"\$*:WARNING\" | sed \"s/\\\$/ /\" >>\$Messagefile
}
note() {
echo \"\$*:NOTE\" | sed \"s/\\\$/ /\" >>\$Messagefile
}
verbose() {
echo \"\$*:VERBOSE\" | sed \"s/\\\$/ /\" >>\$Messagefile
}
debugnote() {
echo \"\$*:DEBUGNOTE\" | sed \"s/\\\$/ /\" >>\$Messagefile
}
error() {
echo \"\$*:ERROR\" | sed \"s/\\\$/ /\" >>\$Messagefile
exit 64
}
stdout() {
echo \"\$*:STDOUT\" | sed \"s/\\\$/ /\" >>\$Messagefile
}'
}
parse_options() { # parse cli options
local Shortoptions Longoptions Parsedoptions Presetoptions Presetfile Parsererror Parsererrorfile
Shortoptions="aAcdDefFghHiIKlmnpPqtTvVwWxXyY"
Longoptions="exe,xonly" # Alternate setups of x11docker
Longoptions="$Longoptions,auto,desktop,tty,wayland,wm::" # Influencing auto-setup of X/Wayland/x11docker
Longoptions="$Longoptions,hostdisplay,nxagent,xdummy,xephyr,xpra,xorg,xvfb,xwin" # X servers
Longoptions="$Longoptions,kwin-xwayland,weston-xwayland,xpra-xwayland,xwayland" # X servers depending on a Wayland compositor
Longoptions="$Longoptions,hostwayland,kwin,weston" # Wayland compositors without X
Longoptions="$Longoptions,border::,dpi:,fullscreen,output-count:,rotate:,scale:,size:,xfishtank" # X/Wayland appearance options
Longoptions="$Longoptions,clean-xhost,display:,keymap:,no-auth,vt:,westonini:,xhost:,xoverip,xtest::" # X/Wayland config
Longoptions="$Longoptions,enforce-i,preset:,pull::,pw:" # x11docker config
Longoptions="$Longoptions,cachebasedir:,home::,homebasedir:,share:" # Host folders
Longoptions="$Longoptions,alsa::,clipboard,gpu,lang::,printer::,pulseaudio::,webcam" # Host integration features
Longoptions="$Longoptions,env:,mobyvm,name:,no-entrypoint,runtime:,workdir:" # Container config
Longoptions="$Longoptions,cap-default,hostipc,limit::,newprivileges::,network::" # Container capabilities
Longoptions="$Longoptions,group-add:,hostuser:,sudouser,user:,shell:" # Container user
Longoptions="$Longoptions,dbus::,init::,hostdbus,sharecgroup" # Container init and DBus
Longoptions="$Longoptions,stdin,interactive" # Container interaction
Longoptions="$Longoptions,runasuser:,runfromhost:,runasroot:" # Additional commands to execute
Longoptions="$Longoptions,showenv,showid,showinfofile,showpid1,showcache" # Output of vars on stdout
Longoptions="$Longoptions,debug,quiet,verbose" # Verbose options
Longoptions="$Longoptions,cleanup,help,launcher,licence,license,version,wmlist" # Special options without starting X or container
Longoptions="$Longoptions,install,remove,update,update-master" # Installation
#
Longoptions="$Longoptions,iglx,keepcache,no-setup,podman,xcomposite,xopt:,xorgconf:,runx" # Experimental
Longoptions="$Longoptions,dbus-system,homedir:,hostnet,no-internet,no-xhost,sharedir:,sharessh,systemd" # Deprecated
Longoptions="$Longoptions,cachedir:,no-init,nothing,no-xtest,openrc,ps,runit,silent,starter,stderr,stdout" # Removed
Longoptions="$Longoptions,sys-admin,sysvinit,tini,trusted,untrusted,vcxsrv" # Removed
Parsererrorfile="/tmp/x11docker.parserserror.$Cachenumber"
Parsedoptions="$(getopt --options "$Shortoptions" --longoptions "$Longoptions" --name "$0" -- "$@" 2>$Parsererrorfile)"
[ -e $Parsererrorfile ] && Parsererror=$(cat $Parsererrorfile) && rm $Parsererrorfile
[ "$Parsererror" ] && error "$Parsererror"
eval set -- "$Parsedoptions"
[ -z "$Parsedoptions_global" ] && Parsedoptions_global="$Parsedoptions"
[ "$*" = "-h --" ] && usage && exit 0 # Catch single -h for usage info, otherwise it means --hostdisplay
[ "$*" = "--" ] && usage && exit 0 # x11docker without options
[ "$*" = "-- --" ] && usage && exit 0 # x11docker-gui without options
while [ $# -gt 0 ]; do
case "${1:-}" in
--hostdisplay|--hostwayland|--kwin|--kwin-xwayland|--nxagent|--tty|--weston|--weston-xwayland|--xpra-xwayland|--xephyr|--xorg|--xwayland|--xwin|-h|-H|-K|-n|-t|-T|-Y|-y|-A|-x|-X)
[ -n "$Xserver" ] && note "Please use only one X server or Wayland option at a time.
You have set option '${1:-}' after option '$Xserver'.
${1:-} will take effect."
;;
--xpra|--xvfb|--xdummy|-a)
case $Xserver in
"") ;;
--xpra|-a) ;;
--xvfb|--xdummy)
[ "${1:-}" != "--xpra" ] && note "Please use only one X server or Wayland option at a time.
You have set option '${1:-}' after option '$Xserver'.
${1:-} will take effect."
;;
*)
note "Please use only one X server or Wayland option at a time.
You have set option '${1:-}' after option '$Xserver'.
${1:-} will take effect."
;;
esac
;;
esac
case "${1:-}" in
--help) usage ; exit 0 ;; # Show help/usage and exit
--license|--licence) license ; exit 0 ;; # Show MIT license and exit
--version) echo $Version ; exit 0 ;; # Output version number and exit
--wmlist) echo $Wm_all ; exit 0 ;; # Special option for x11docker-gui to retrieve list of window managers
-e|--exe) X11dockermode="exe" ;; # Execute application from host instead of running docker image
--xonly) X11dockermode="xonly" ;; # Only create X server
#### Predefined option sets
--preset) Presetoptions=""
Presetfile=""
[ -f "$Presetdirsystem/${2:-}" ] && Presetfile="$Presetdirsystem/${2:-}"
[ -f "$Presetdirlocal/${2:-}" ] && Presetfile="$Presetdirlocal/${2:-}"
[ -f "/${2:-}" ] && Presetfile="/${2:-}"
[ -f "$Presetfile" ] || error "Option --preset: File not found: ${2:-}
Searching as an absolute path and in:
$Presetdirlocal
$Presetdirsystem"
Presetoptions="$(sed '/^#/d' < "$Presetfile" | tr '\n' ' ')"
debugnote "--preset ${2:-}:
$Presetoptions"
[ "$Presetoptions" ] && eval parse_options $Presetoptions
shift ;;
#### Choice of X servers and Wayland compositors
--auto) Autochooseserver="yes" ;; # Default: auto-choose X server or Wayland compositor
-h|--hostdisplay) Xserver="--hostdisplay" ;; # Host display :0 with shared X socket
-H|--hostwayland) Xserver="--hostwayland" ;; # Host wayland. Allows coexistence with option
-K|--kwin) Xserver="--kwin" ;; # KWin, Wayland only
--kwin-xwayland) Xserver="--kwin-xwayland" ;; # KWin + Xwayland
-n|--nxagent) Xserver="--nxagent" ;; # nxagent
--runx) Xserver="--runx" ;; # MS Windows: Will be Xwin or VcXsrv
-t|--tty) Xserver="--tty" ;; # Do not provide any X nor Wayland
-T|--weston) Xserver="--weston" ;; # Weston, Wayland only
-Y|--weston-xwayland) Xserver="--weston-xwayland" ;; # Weston + Xwayland
--xdummy) [ "$Xserver" = "--xpra" ] && Xpravfb="Xdummy" || Xserver="--xdummy" ;; # Xdummy. Invisible on host.
-y|--xephyr) Xserver="--xephyr" ;; # Xephyr
-a|--xpra) [ "$Xserver" = "--xdummy" ] && Xpravfb="Xdummy"
[ "$Xserver" = "--xvfb" ] && Xpravfb="Xvfb"
Xserver="--xpra" ;; # xpra
-A|--xpra-xwayland) Xserver="--xpra-xwayland" ;; # Xpra with vfb Xwayland
-x|--xorg) Xserver="--xorg" ;; # Xorg
--xvfb) [ "$Xserver" = "--xpra" ] && Xpravfb="Xvfb" || Xserver="--xvfb" ;; # Xvfb. Invisible on host.
-X|--xwayland) Xserver="--xwayland" ;; # Xwayland on already running Wayland
--xwin) Xserver="--xwin" ;; # XWin, MS Windows only
#### Influencing automatical choice of X server or Wayland compositor
-d|--desktop) Desktopmode="yes" ;; # image contains a desktop environment.
-g|--gpu) Sharegpu="yes" ;; # share files in /dev/dri, allow GPU usage
-W|--wayland) Setupwayland="yes" ;; # set up wayland environment, regards --desktop
-w) Windowmanagermode="auto" ;;
--wm) case "${2:-}" in # choose window manager
"n"|"none") Windowmanagermode="none" ;;
"host") Windowmanagermode="host" ;;
"container") Windowmanagermode="container" ;;
""|"auto"|"m") Windowmanagermode="auto" ;;
*) Windowmanagermode="auto"; Windowmanagercommand="${2:-}" ;;
esac
shift ; Desktopmode="yes" ;;
#### X and Wayland appearance
--border) Xpraborder="${2:-"blue,1"}"; shift ;; # Colored border for xpra clients
--dpi) Dpi=${2:-} ; shift ;; # Dots per inch. Influences font size
-f|--fullscreen) Fullscreen="yes" ;; # Fullscreen mode for Xephyr and Weston
--output-count) Outputcount="${2:-}" ; shift ;; # Number of virtual outputs
--rotate) Rotation=${2:-} ; shift ;; # Rotation and mirroring
--scale) Scaling=${2:-} ; shift ;; # Zoom
--size) Screensize="${2:-}" ; shift ;; # Screen size
-F|--xfishtank) Xfishtank="yes" ;; # Run xfishtank on new X server
#### X and Wayland configuration
--display) Newdisplaynumber="${2:-}" # Display number to use for new X server or Wayland compositor
[ "$(echo $Newdisplaynumber | cut -c1)" = ":" ] && Newdisplaynumber="$(echo $Newdisplaynumber | cut -c2-)"
shift ;;
--keymap) Xkblayout="${2:-}" ; shift ;; # Keymap layout for xkbcomp. Compare /usr/share/X11/xkb/symbols
--vt) Newxvt="${2:-}" ; shift ;; # Virtual console to use for --xorg
--xoverip) Xoverip="yes" ;; # Use X over TCP/IP instead of sharing X socket
--xtest) case "${2:-}" in # X extension XTEST
yes|"") Xtest="yes" ;;
no) Xtest="no" ;;
*) warning "Invalid argument for option --xtest [=yes|no]: ${2:-}" ;;
esac; shift ;;
--westonini) Customwestonini="${2:-}" ; shift ;; # Custom weston.ini
#### X Authentication
--clean-xhost|--no-xhost) Cleanxhost="yes" # Disable xhost credentials on host X
[ "${1:-}" = "--no-xhost" ] && note "Option --no-xhost is deprecated.
Please use --clean-xhost instead." ;;
--no-auth) Xauthentication="no" ;; # Disable cookie authentication on new X, set xhost +. Use for debugging only
--xhost) Xhost="$2" ; shift ;; # Custom xhost setting on new X server
#### Host integration options
--alsa) Sharealsa="yes" # ALSA sound (shares /dev/snd)
Alsacard="${2:-$Alsacard}" ; shift ;;
-c|--clipboard) Shareclipboard="yes" ;; # Clipboard sharing
-l) Langwunsch="$Langwunsch
${LANG:-}" # Locale/language setting
Langwunsch="${Langwunsch:-$LC_ALL}"
[ "$Langwunsch" ] || note "Option --lang: Environment variable \$LANG is empty.
Please specify desired language locale with e.g. --lang=en_US or --lang=zh_CN." ;;
--lang) Langwunsch="$Langwunsch
${2:-${LANG:-}}" ; shift # Locale/language setting
Langwunsch="${Langwunsch:-$LC_ALL}"
[ "$Langwunsch" ] || note "Option --lang: Environment variable \$LANG is empty.
Please specify desired language locale with e.g. --lang=en_US or --lang=zh_CN." ;;
-P|--printer) Sharecupsmode="${2:-auto}" ; shift ;; # Printer sharing with CUPS
-p) Pulseaudiomode="auto" ;; # Pulseaudio sound
--pulseaudio) Pulseaudiomode="${2:-auto}"; shift ;; # Pulseaudio sound
--webcam) Sharewebcam="yes" ;; # Webcam sharing
#### Special options
--enforce-i) ;; # Run in bash interactive mode. Parsed at begin of script, nothing to do here.
-i|--interactive) Interactive="yes" ;; # Interactive terminal
--pull) Pullimage="${2:-yes}" ; shift ;; # Allow 'docker pull'
--pw) Passwordfrontend="${2:-}" ; shift ;; # Password prompt frontend
--runasroot) Runasroot="$Runasroot
${2:-}" ; shift ;; # Add custom root command in container setup script
--runasuser) Runasuser="$Runasuser
${2:-}"
shift ;; # Add custom user command in cmdrc
--runfromhost) Runfromhost="$Runfromhost
${2:-}" ; shift ;; # Add custom host command in xinitrc
#### User settings
--group-add) Containerusergroups="$Containerusergroups ${2:-}" ; shift ;; # Additional groups for container user
--hostuser) Hostuser="${2:-}" ; shift ;; # Set host user different from logged in user
--shell) Containerusershell="${2:-}" ; shift ;; # Set preferred user shell
--sudouser) Sudouser="yes" ;; # su and sudo for container user with password x11docker
--user) Containeruser="${2:-}" ; shift # Set container user other than host user
[ "$Containeruser" = "RETAIN" ] && Createcontaineruser="no" ;;
#### Init system and DBus
--dbus) Dbusrunsession="${2:-yes}" ; shift ;; # DBus in container, Default: user session, =system: with system daemon
--hostdbus) Sharehostdbus="yes" ;; # Connect to host DBus
--init) Initsystem="${2:-tini}" ; shift ;; # init in container
--sharecgroup) Sharecgroup="yes" ;; # Share /sys/fs/cgroup. Default for --init=systemd, possible use with --init=openrc or elogind.
--systemd) Initsystem="systemd" ; note "Option --systemd is deprecated. Please use: --init=systemd" ;;
#### Container configuration
--cap-default) Capdropall="no" ;; # Don't use --cap-drop=ALL --security-opt=no-new-privileges
--env) store_runoption env "${2:-}" # Set container environment variables
shift ;;
--hostipc) Sharehostipc="yes" ;; # docker run option --ipc=host
--limit) Limitresources="${2:-0.5}" ; shift ;; # Limited CPU and RAM access
--mobyvm) Mobyvm="yes" ;; # Use MobyVM in WSL2
--name) Containername="${2:-}" ; shift ;; # Set container name
-I) Network="" ;;
--network) Network="${2:-}" ; shift ;;
--newprivileges) Allownewprivileges="${2:-yes}" ; shift ;; # [Don't] set --security-opt=no-new-privileges
--no-entrypoint) Noentrypoint="yes" ;; # Disable ENTRYPOINT of image
--runtime) Runtime="${2:-}" ; shift # Runtime=runc|nvidia|kata-runtime|crun
[ "$Runtime" = "kata" ] && Runtime="kata-runtime" ;;
--stdin) Forwardstdin="yes" ;; # Forward stdin to container command
--workdir) Workdir="${2:-}" ; shift ;; # Set working directory
#### host folders and docker volumes
-m) Sharehome="host" ;;
--home|--homedir) Sharehome="yes" # Share host folder as HOME in container, ~/x11docker/imagename or $2
[ "${1:-}" = "--homedir" ] && note "Option --homedir is deprecated.
Please use --home=DIR instead."
Persistanthomevolume="${2:-}" ; shift ;;
--share|--sharedir) store_runoption volume "${2:-}" # Share host file, device or directory
[ "${1:-}" = "--sharedir" ] && note "Option --sharedir is deprecated.
Please use option --share=PATH instead."
shift ;;
--homebasedir) Hosthomebasefolder="${2:-}" ; shift ;; # Set base folder for --home instead of ~/.local/share/x11docker
--cachebasedir) Cachebasefolder="${2:-}" ; shift ;; # Set base folder for cache instead of ~/.cache/x11docker
#### Verbosity options
-D|--debug) Debugmode="yes" ;; # Debugging mode
--showinfofile) Showinfofile="yes" ;; # Show path to $Storeinfofile
-v|--verbose) Verbose="yes" ;; # Be verbose
-V) Verbose="yes"; Verbosecolors="yes";; # Be verbose with colored output
-q|--quiet) Silent="yes" ;; # Do not show warnings or errors
--showcache) Showcache="yes" ;; # Output of $Cachefolder. For x11docker-gui only
--showenv) Showdisplayenvironment="yes" ;; # Output of display number and cookie file on stdout. Catch with: read xenv < <(x11docker --showenv)
--showid) Showcontainerid="yes" ;; # Output of container id on stdout
--showpid1) Showcontainerpid1pid="yes" ;; # Output of host PID of container PID 1
#### Special options not starting X or docker
--cleanup) Cleanup="yes" ;; # Remove orphaned containers and cache files
--install|--update|--update-master|--remove) Installermode="${1:-}" ;; # Installer
--launcher) Createlauncher="yes" ;; # Create application launcher on desktop and exit
#### Experimental options
--iglx) Iglx="yes" ; note "Option --iglx: experimental option." ;; # Indirect rendering; broken since Xorg ~18.2
--keepcache) Preservecachefiles="yes" ; note "Option --keepcache: experimental option." ;;
--no-setup) Containersetup="no" ; note "Option --no-setup: experimental option." ;;
--podman) Podman="yes" ; note "Option --podman: experimental option.
Please report issues at: https://github.com/mviereck/x11docker/issues/255" ;;
--xcomposite) Xcomposite="yes" ; note "Option --xcomposite: experimental option." ;; # Enable X extension COMPOSITE
--xopt) Xserveroptions="${2:-}" ; note "Option --xopt: experimental option." ; shift ;; # Custom X server options
--xorgconf) Xorgconf="${2:-}" ; note "Option --xorgconf: experimental option." ; shift ;; # Custom xorg.conf
#### Deprecated options
--dbus-system) note "Option --dbus-system is deprecated.
Please use one of --init=systemd|openrc|runit|sysvinit instead.
Fallback: Enabling options --dbus=system --cap-default"
check_fallback
Dbusrunsession="system"
Capdropall="no" ;;
--hostnet) Network="host"
note "Option --hostnet is deprecated.
Please use --network=host instead." ;;
--no-internet) Network="none"
note "Option --no-internet is deprecated.
Please use --network=none instead." ;;
--sharessh) [ -e "${SSH_AUTH_SOCK:-}" ] && { # SSH socket sharing
store_runoption volume "$(dirname $SSH_AUTH_SOCK)"
store_runoption env "SSH_AUTH_SOCK=$(escapestring "${SSH_AUTH_SOCK:-}")"
} || note "Option --sharessh: environment variable \$SSH_AUTH_SOCK not set:" ;
note "Option --sharessh is deprecated.
Please use (directly or with help of option --preset):
--share \$(dirname \$SSH_AUTH_SOCK) --env SSH_AUTH_SOCK=\"\$SSH_AUTH_SOCK\"" ;;
#### Removed options
--vcxsrv) error "Option --vcxsrv is no longer supported.
Please use either option --xwin in Cygwin/X
or run x11docker with runx in WSL or MSYS2.
For 'runx' look at: https://github.com/mviereck/runx" ;;
--no-init|--openrc|--runit|--sysvinit|--tini)
error "Option ${1:-} has been removed.
Please use option --init=INITSYSTEM instead." ;;
--cachedir|--nothing|--no-xtest|--ps|--silent|--starter|--stderr|--stdout|--sys-admin|--trusted|--untrusted)
error "Option ${1:-} has been removed.
Please have a look at 'x11docker --help' for possible replacements
or search for '${1:-}' in /usr/share/doc/x11docker/CHANGELOG.md." ;;
##### Custom docker options / image name + container command. Everything after --
--)
shift
[ "$(cut -c1 <<< "${1:-}")" = "-" ] && grep -q " -- " <<< " $* " && {
while [ $# -gt 0 ] ; do
[ "${1:-}" = "--" ] && shift && break
Customdockeroptions="$Customdockeroptions '${1:-}'"
shift
done
}
while [ $# -gt 0 ] ; do
[ -n "${1:-}" ] && [ -z "$Imagename" ] && [ "$(echo "${1:-}" | cut -c1)" = "-" ] && Customdockeroptions="$Customdockeroptions ${1:-}"
[ -n "${1:-}" ] && [ -z "$Imagename" ] && [ "$(echo "${1:-}" | cut -c1)" != "-" ] && Imagename="${1:-}" && shift
[ -n "${1:-}" ] && [ -n "$Imagename" ] && Containercommand="$Containercommand '${1:-}'"
shift
done
;;
'') ;;
*) error "Unknown option ${1:-}
Parsed options:
$Parsedoptions" ;;
esac
shift
done
Customdockeroptions="$(sed "s/--cap-add' '/--cap-add=/" <<< "$Customdockeroptions")"
Customdockeroptions="$(sed "s/--runtime' '/--runtime=/" <<< "$Customdockeroptions")"
grep -q -- "--runtime.kata" <<< "$Customdockeroptions" && Runtime="kata-runtime"
grep -q -- "--runtime.nvidia" <<< "$Customdockeroptions" && Runtime="nvidia"
grep -q -- "--runtime.runc" <<< "$Customdockeroptions" && Runtime="runc"
grep -q -- "--runtime.crun" <<< "$Customdockeroptions" && Runtime="crun"
[ -n "$Xserver" ] && Autochooseserver="no"
return 0
}
unpriv() { # run a command as unprivileged user. Needed if x11docker was started by root or with sudo.
# $Unpriv is declared in check_hostuser: 'eval' or 'su $Hostuser -c'
$Unpriv "${1:-}"
}
main() {
trap finish EXIT
trap finish_sigint SIGINT
exec {FDstderr}>&2 # stderr channel for warning(), error(), note(), debugnote() and --verbose
declare_variables
parse_options "$@"
[ "$Silent" = "yes" ] && exec {FDstderr}>/dev/null # --quiet
[ "$Debugmode" = "yes" ] && { # --debug
set -Eu
trap 'traperror $? $LINENO $BASH_LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]})' ERR
}
# check host, create cache
check_host # get some infos about host system #time0,234
check_runmode # modes: run image, or host command, or X only # --exe, --xonly
check_hostuser # find unprivileged host user # --hostuser
create_cachefiles # create cache files owned by unprivileged user # --cachebasedir
check_hostxenv # check X environment from host
storeinfo "x11dockerpid=$$" # store pid of x11docker
debugnote "
x11docker version: $Version
docker version: $($Dockerexe --version 2>&1)
Host system: $(grep '^PRETTY_NAME' /etc/os-release 2>/dev/null | cut -d= -f2 || echo "$Hostsystem")
Host architecture: $Hostarchitecture
Command: '$0' $(for Line in "$@"; do echo -n "'$Line' " ; done)
Parsed options: $Parsedoptions_global"
# Special x11docker jobs
[ "$Createlauncher" = "yes" ] && { create_launcher ; exit ; } # --launcher: Create application launcher icon on desktop
[ "$Cleanup" = "yes" ] && { cleanup ; exit ; } # --cleanup: Clean up cache and orphaned x11docker containers
[ "$Installermode" ] && { # --install, --update, --update-master, --remove
[ "$Startuser" = "root" ] || [ "$Winsubsystem" = "CYGWIN" ] || [ "$Winsubsystem" = "MSYS2" ] || error "Must run as root to install, update or remove x11docker."
installer $Installermode
exit
}
# check options
check_xserver # check chosen X server or auto-choose one
check_option_interferences # check options, change settings if needed
option_messages # some messages depending on options, but not changing anything
# container user
check_containeruser # unprivileged user in container # --user
check_containerhome # create persistant container home # --home, --homebasedir
# some checks and setup
drop_cachefiles # remove cachefiles not needed for current setup
setup_verbosity # create [and show] summary logfile # --verbose
setup_fifo # open message channels for container, dockerrc, xinitrc and watchpidlist()
check_screensize # size of host X and of new X server # --size #time0,213
[ "$Windowmanagermode" ] && check_windowmanager # --wm
[ "$Sharegpu" = "yes" ] && setup_gpu # --gpu
[ "$Sharewebcam" = "yes" ] && setup_webcam # --webcam
[ "$Sharecupsmode" ] && setup_printer # --printer
[ "$Pulseaudiomode" ] && setup_sound_pulseaudio # --pulseaudio
[ "$Sharealsa" = "yes" ] && setup_sound_alsa # --alsa
[ "$Cleanxhost" = "yes" ] && clean_xhost # --clean-xhost
#### Create command to run X server [and/or Wayland compositor]
[ "$Xserver" != "--xorg" ] && [ -n "$Newxvt" ] && note "Option --vt only takes effect with option --xorg."
[ "$Xserver" = "--xorg" ] && [ -z "$Newxvt" ] && check_vt # --vt: find free tty/virtual terminal for Xorg
{ [ "$Xserver" = "--xdummy" ] || [ "$Xpravfb" = "Xdummy" ] ; } && create_xdummyxorgconf && create_xdummywrapper
check_newxenv # find free display, create $Newxenv
create_xcommand # set up start command for X server # all X server and Wayland options
[ "$Xcommand" ] && debugnote "X server command:
$Xcommand"
[ "$Compositorcommand" ] && debugnote "Compositor command:
$Compositorcommand"
[ "$Shareclipboard" = "yes" ] && setup_clipboard # --clipboard
check_terminalemulator # find terminal emulator like xterm for error messages and 'docker pull'
check_passwordfrontend # check for su/sudo/gksu/pkexec etc. # --pw #time0,230
setup_initsystem # init in container. Default: tini # --init
[ "$Runsinterminal" = "no" ] && [ "$Passwordneeded" = "yes" ] && warning "You might need to run x11docker in terminal
for password prompt if prompting for password with a GUI fails."
debugnote "Users and terminal:
x11docker was started by: $Startuser
As host user serves (running X, storing cache): $Hostuser
Container user will be: $( [ "$Createcontaineruser" = "yes" ] && echo $Containeruser || echo "(retaining USER of image)")
Container user password: $( [ "$Createcontaineruser" = "yes" ] && echo x11docker || echo "(unknown)")
Getting permission to run docker with: $Passwordcommand $Sudo
Terminal for password frontend: $Passwordterminal
Running in a terminal: $Runsinterminal
Running on console: $Runsonconsole
Running over SSH: $Runsoverssh
Running sourced: $Runssourced
bash \$-: $-"
[ "$Winsubsystem" ] && debugnote "
Running on Windows subsystem: $Winsubsystem
Path to subsystem: $(convertpath windows $Winsubpath)/
Mount path in subsystem: $Winsubmount/
Using MobyVM: $Mobyvm"
#### Create docker command
[ "$X11dockermode" = "run" ] && {
# core setup of docker command
setup_capabilities # add linux capabilities if needed for some options. Default: --cap-drop=ALL
[ "$Sharehostdbus" = "yes" ] && setup_hostdbus # --hostdbus
create_dockercommand # create 'docker run' command #time0,631
echo "$Dockercommand" >> $Dockercommandfile
debugnote "Docker command:
$Dockercommand"
#### Create helper scripts to set up container
## dockerrc runs as root (or member of group docker) on host.
# Main jobs: check image, pull image if needed, create script containerrc to run container command
create_dockerrc
verbose "Generated dockerrc:
$(nl -ba <$Dockerrc)"
## containerrootrc runs as root in container.
# Main jobs: create unprivileged container user, disable possible privilege leaks, set local time.
# Optional jobs: run init system, run DBus daemon, install nvidia driver, create language locale.
[ "$Containersetup" = "yes" ] && {
create_containerrootrc
verbose "Generated containerrootrc:
$(nl -ba <$Containerrootrc)"
}
create_xtermrc # xtermrc to prompt for password if needed.
}
#### Create helper script xinitrc to set up X
## xinitrc is started by xinit and does some setup within new X server.
# Main job: create cookie, check xhost, set keyboard layout.
# Optional jobs: run window manager, run xfishtank, run host command, share clipboard, scale/rotate --xorg, create set of screen resolutions.
create_xinitrc
verbose "Generated xinitrc:
$(nl -ba <$Xinitrc)"
[ -s "$Westonini" ] && verbose "Generated weston.ini:
$(nl -ba <$Westonini)"
{ #### Run docker image
# For code flow logic, start_xserver() should run here first and be moved to background.
# For technical reasons, xinit must not run in a subshell:
# --xorg on tty only works if xinit runs in foreground to grab the tty.
# Otherwise, Xwrapper.config must be edited to 'allowed_users=anybody' even on console.
# Thus docker runs in this subshell after X server is ready to accept connections.
# Waiting for X is done in dockerrc.
trap '' SIGINT
# start container
case $X11dockermode in
run) start_docker ;; # (default)
exe) start_hostexe ;; # --exe, --xonly
esac
Pid1pid="$(storeinfo dump pid1pid)"
# watch container
case $X11dockermode in
run)
case $Mobyvm in
no) setonwatchpidlist "${Pid1pid:-NOPID}" pid1pid ;;
yes) setonwatchpidlist "CONTAINER$Containername" ;;
esac
;;
exe) setonwatchpidlist "${Pid1pid:-NOPID}" pid1pid ;;
esac
# watch xinit and X server
case $Xserver in
--tty|--hostdisplay|--hostwayland|--weston|--kwin) ;;
*)
Xinitpid="$(pgrep -a xinit 2>/dev/null | grep "xinit $Xinitrc" | awk '{print $1}')"
checkpid "$Xinitpid" && setonwatchpidlist $Xinitpid xinit
echo $Xcommand | grep -q Xorgwrapper && Line="Xorg $Newdisplay" || Line="$(head -n1 <<< "$Xcommand" | tr -d '\\')"
Xserverpid=$(ps aux | rmcr | grep "$(echo "${Line:-nothingtolookfor}" | cut -d' ' -f1-2)" | grep -v grep | grep -v xinit | awk '{print $2}')
checkpid "$Xserverpid" && setonwatchpidlist "$Xserverpid" Xserver
;;
esac
[ "$Pulseaudiomode" = "tcp" ] && start_pulseaudiotcp # --pulseaudio=tcp
# some debug output
checkpid "$Pid1pid" && debugnote "Process tree of ${Hostexe:-container}: (maybe not complete yet)
$(pstree -cp $Pid1pid 2>&1 ||:)"
debugnote "Process tree of x11docker:
$(pstree -p $$ 2>&1 ||:)
$(storepid test dockerstopshell && echo "Lost child of dockerrc (dockerstopshell):
$(pstree -p $(storepid dump dockerstopshell) 2>&1 ||:)")"
debugnote "storeinfo(): Stored info:
$(cat $Storeinfofile)"
debugnote "storepid(): Stored pids:
$(cat $Storepidfile)"
# optional info on stdout
[ "$Showinfofile" = "yes" ] && echo "$Storeinfofile" # --showinfofile
[ "$Showcache" = "yes" ] && echo "$Cachefolder" # --showcache (x11docker-gui only)
[ "$Showdisplayenvironment" = "yes" ] && echo "$(storeinfo dump Xenv)" # --showenv
[ "$Showcontainerid" = "yes" ] && echo "$(storeinfo dump containerid)" # --showid
[ "$Showcontainerpid1pid" = "yes" ] && echo "$Pid1pid" # --showpid1
storeinfo "x11docker=ready"
} <&0 & storepid $! containershell
#### Start X server [and/or Wayland compositor] [and xpra]
waitforlogentry "start_xserver()" $Storeinfofile "readyforX=ready" "" infinity
[ "$Xpraservercommand" ] && {
{
waitforlogentry xpra $Storeinfofile "xinitrc=ready" infinity
rocknroll || exit 0
start_xpra # --xpra, --xpra-xwayland
} & storepid $! xpraloop
}
rocknroll && [ "$Compositorcommand" ] && start_compositor
rocknroll && start_xserver
saygoodbye main
}
main "$@"