mirror of
https://github.com/pumpitupdev/pumptools.git
synced 2024-11-24 07:00:09 +01:00
d7b03a935a
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
9002 lines
383 KiB
Bash
Executable File
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 "$@"
|