#!/bin/bash # x11docker V 1.0 16.10.2015 # usage() { # --help: show usage information echo " x11docker: Create a new X server & run GUI applications and desktop environments in docker, and show them on your display. Useful to avoid security issues concerning X forwarding. Doesn't have dependencies inside of docker images. If using X11 server from Xorg (default): * To start x11docker from console, switch to tty1 with . * To start x11docker from within X11, first run 'dpkg-reconfigure x11-common' and choose option 'anybody'. * To switch between displays/X servers press If using Xpra or Xephyr (see options '--xpra' and '--xephyr'): * The dockered applications will appear on your normal display. (needs no tty switching, 'x11-common' doesn't need to be changed) Usage: To run a docker image with new X server: x11docker [OPTIONS] IMAGE [COMMAND] 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 a new X server with window manager: x11docker [OPTIONS] * The new X server will be terminated when [docker] application is closed. * On systems without a root password, like Ubuntu, use option '--sudo'. Options: -h, --help display this message and exit -w, --wm COMMAND window manager to use (otherwise, x11docker tries to run a new instance of the current running window manager; if that's not possible, it will look for a recommended one) -d, --desktop don't run any window manager on new X server; image contains its own desktop and/or window manager -x use Xpra or Xephir to show docker applications on display :0 instead of switching to another tty (automated decision depending on option '-d, --desktop') -a, --xpra use Xpra to show application windows on display :0 (needs packages 'xpra' and 'xvfb' to be installed) -y, --xephyr use Xephyr to show desktops in a window on display :0 (needs package 'xephyr' to be installed) -c, --clipboard share clipboard between X servers (Xpra only) -f, --fullscreen run Xephyr in fullscreen mode -z, --size XxY set Xephyr screen size (f.e. 800x600 ) -r, --resizeable make Xephyr window resizeable (Note: some window managers/desktops won't like this and may misbehave) -e, --exe execute host application on new X server (no docker) -u, --hostuser create a user in the container similar to the host user (same UID, same GID, same name) (experimental) -o, --sudouser same as in '--hostuser', but container user gains root permissions via sudo without password -m, --home share folder ~/x11docker/IMAGENAME (in home folder) with created container. For use with option '--hostuser' Advanced options: -s, --sudo use 'sudo' to run 'su -c docker ...' (Ubuntu special) --starter create starter on desktop & exit -p, --ps preserve container on exit (as a default, x11docker sets docker run option '--rm' to remove created container) --root allow root to run x11docker. Default: No (A new X server should not run as root) --command only show created docker command on stdout and exit --cid show filename that contains container ID on stdout --cache don't delete cache files on exit (including logs) -v, --verbose be verbose -V, --VERBOSE be very verbose Dependencies: depending on choosed options, x11docker needs some packages to be installed. It will check for them on startup and show messages if some are missing. List of possible needed packages: docker.io xorg wmctrl xpra xephyr xvfb Window manager: x11docker works fine with most common window managers. x11docker tries to detect your current running window manager to start a new instance of it on the new display. Otherwise, choose one you like. Recommendations: * Lightweight and desktop independent: $WM_RECOMMENDED_DESKTOP_LIGHT * Heavy and desktop independent: $WM_RECOMMENDED_DESKTOP_HEAVY * Lightweight, no desktop options: (if you run only X server and window manager, you would need an exit/logout option) $WM_RECOMMENDED_NODESKTOP_LIGHT * Heavy, no desktop options: $WM_RECOMMENDED_NODESKTOP_HEAVY _NOT_ recommended: * Known to have problems in some situations: $WM_UGLY * Known not to work at all: $WM_BAD " } { # variable description and default settings CONFIGFOLDER=$HOME/.config/x11docker # define config file folder. Will be changed to path of subfolder CACHEFOLDER=$HOME/.cache/x11docker # define cache folder to store temporary files X11DOCKERRC=$CONFIGFOLDER/x11dockerrc # config file to preset options X11DOCKERMODE="" # can be either "onlyX", "run" or "exe", depends on options DOCKERARGS="" # arguments to submit to docker DOCKEROPTIONS="" # options for docker without image name/image args X11DOCKERARGS="$@" # arguments for x11docker without arguments for docker ARGUMENT="" # variable to step through arguments IMAGENAME="" # name of image to run IMAGECOMMAND="" # name of image [+ commands+args] DOCKERIP="" # IP adress of docker interface DOCKERPID="123454321" # process ID (PID) of docker instance. set a dummy here XINITRC=xinitrc # file to store xinitrc commands x11docker [OPTIONS] -- [DOCKER_RUN_OPTIONS] IMAGE [COMMAND [ARG1 ARG2 ...]] XINITLOGFILE=xinit.log # file to log output of X server XTERMRC=xtermrc # file to store xtermrc commands XTERMLOGFILE=xterm.log # file to log output of xterm DOCKERLOGFILE=docker.log # file to log output of docker DOCKERPIDFILE=docker.pid # file to store process ID of docker CIDFILE=containerID # file to store container ID number BGPIDFILE=bgpids # file to store PIDS and names of background processes that shut be killed on exit XSERVER="X11" # X server to use ( one of X11 | XPRA | XEPHIR) STARTXAUTHORITY=$XAUTHORITY # store Environment variable pointing to X authority cookies STARTDISPLAY=$DISPLAY # store Environment variable containing name of current display NEWDISPLAY="" # new display for new X server XSERVERCOOKIE=Xservercookie # file to store new X server cookies XCLIENTCOOKIE=Xclientcookie # file to store new X client cookies #NEWXLOCK="" # .Xn-lock - exists for running X server with socket n #NEWXSOCKET="" # New X socket # constant options for different X servers X11ARGS="-quiet -retro +extension Composite" # not changing defaults for X11 XPRAARGS="--no-pulseaudio --speaker --no-microphone" # not changing defaults for Xpra XEPHYRARGS="-retro +extension Composite -glamor" # not changing defaults for Xephyr XVFBARGS="+extension Composite" # not changing defaults for Xvfb # special for Xpra: XPRASERVERLOGFILE=xpraserver.log # logfile for Xpra server ZEIT="" # for counting time until Xpra server is ready # special for Xephyr WINDOWTITLE="" # window title for Xephyr. Will be filled while parsing options # Get screen size for use with Xephyr XAXIS=$(xrandr --current | grep '*' | tail -1 | awk '{print $1}' | cut -d 'x' -f1) YAXIS=$(xrandr --current | grep '*' | tail -1 | awk '{print $1}' | cut -d 'x' -f2) XEPHYRRESIZEABLE="no" # option '-r, --resizable' XEPHYRSIZE="-screen $(($XAXIS-50))"x"$(($YAXIS-90))" # option '-z, --size' can change this value. otherwise, a nearly maximized window size is set here XEPHYRFULLSCREEN="no" # option '-f, --fullscreen': use fullscreen mode (Xephyr only) # variables depending on options WINDOWMANAGER="" # option '-w, --wm': window manager to use DESKTOPIMAGE="no" # option '-d, --desktop': if "yes", run desktop environment instead of single application AUTOCHOOSESERVER="no" # option '-x': automated choosing server SHARECLIPBOARD="no" # option '-c, --clipboard' can allow clipboard sharing for Xpra # ADDHOSTUSER="no" # option '-u, --hostuser': try to add user to container with same UID and GID as host user ADDSUDOUSER="no" # option '-o, --sudouser': same as '-u, --hostuser', but created user gains root access via sudo without password CREATEUSERSCRIPT=createuser # " " : file to store commands to create container user ADDUSERHOME="no" # option '-m, --home': share a folder ~/.local/share/x11docker/IMAGENAME with created container ADDUSERHOMEFOLDER="" # " " : path to shared folder BENUTZER=$USER # name of user to simulate in container # CREATEDESKTOPSTARTER="no" # option '--starter': create desktop starter and exit yes/no ALLOWROOT="no" # option '--root': allow root to run x11docker ONLYSHOWCOMMAND="no" # option '-c, --command': if not empty, only show docker command and exit GETROOT="su -c" # option '-s, --sudo' will add 'sudo' to command to get root privileges. SHOWCIDFILENAME="no" # option '--cid': if 'yes', show filename of file containing container ID VERBOSE="" # options '-v, --verbose' and '-V,--VERBOSE' be verbose='verbose/'VERBOSE'=be more verbose, ''=be quiet PRESERVECACHEFILES="no" # option '--cache': preserve cache files instead of removing them on exit PRESERVECONTAINER="no" # option '-p, --ps': if yes, preserve container instead of removing it with 'docker run --rm' # strings collecting pids of background jobs to be killed before exiting JOBSTOKILL="" # collections of job pids to kill before exit of script (for 'tail'/option '-V, --VERBOSE') XINITJOBSTOKILL="" # collection of job pids to kill at end of xinitrc # Window manager variables WM_NAME="" # base name of window manager WMCTRL_IS_INSTALLED="" # wmctrl is installed yes/no # # 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 lxsession mate-session mate-wm marco metacity muffin mutter notion olwm olvwm openbox ororobus pekwm" WM_GOOD="$WM_GOOD sawfish twm wmaker w9wm xfwm4" # 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" # these wm's are recommended, heavy AND desktop independent. best first: WM_RECOMMENDED_DESKTOP_HEAVY="mate-session lxsession enlightenment" # these wm's are recommended and lightweight, but cannot show desktop options. best first: WM_RECOMMENDED_NODESKTOP_LIGHT="sawfish xfwm4 metacity mutter" # these wm's are recommended and heavy, but cannot show desktop options (especially exiting themselves). best first: WM_RECOMMENDED_NODESKTOP_HEAVY="kwin compiz cinnamon gnome-shell" # these wm's are not really useful (please don't hit me) (best first): WM_NOT_RECOMMENDED="awesome evilwm herbstluftwm i3 lwm matchbox miwm 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="clfswm tinywm tritium" # List of all working window managers, recommended first: (excluding $WM_BAD) WM_ALL="$WM_RECOMMENDED_DESKTOP_LIGHT $WM_RECOMMENDED_NODESKTOP_LIGHT $WM_RECOMMENDED_DESKTOP_HEAVY $WM_RECOMMENDED_NODESKTOP_HEAVY $WM_GOOD $WM_UGLY" } error() { # show error messages on stderr and exit echo "x11docker ERROR: $*" >&2 echo "Type 'x11docker --help' for usage information" >&2 echo "For debugging, either enable option '-V, --VERBOSE' or/and" >&2 echo "enable option '--cache' and see logfiles in" >&2 echo "$CACHEFOLDER" >&2 echo "" >&2 exit 1 } warning() { # show warning message echo "x11docker WARNING: $*" echo "" } verbose() { # show verbose messages echo "x11docker: $*" echo "" } storepid () { # store pids and names in file $BGPIDFILE # store PID and process name of background processes in file # $1 should be PID, $2 should be name of process # for use on exit / with trap to clean up with background processes # normally, they should be terminated via $PIDSTOKILL/$XINITPIDSTOKILL # this subroutine has a twin in xinitrc echo $1 $2 >> $BGPIDFILE } killbgpids () { # kill background processes listet in $BGPIDFILE # check for possible remaining background processes stored in $BGPIDFILE # double check with PID and name; kill if process is still running # jobs in $JOBSTOKILL and $XINITPIDSTOKILL should already be killed, but will be checked here, too LINE="" # line of $BGPIDFILE PID="" # PID to look for NAME="" # name matching to PID touch $BGPIDFILE while read LINE do PID=$(echo $LINE | awk '{print $1}') NAME=$(echo $LINE | awk '{print $2}') if [ -n "$(ps -A | grep $PID | grep $NAME)" ] ; then warning "Found remaining background process. Will send signal TERM to process $LINE" kill $PID fi done < $BGPIDFILE } finish() { # trap routine, clean up background processes and cache # # kill registered background processes for ARGUMENT in $PIDSTOKILL ; do kill $ARGUMENT ; done # clean up # # kill possible remaining background processes (f.e. on interrupt) killbgpids # # option '--cache': [don't] remove cache files if [ "$PRESERVECACHEFILES" = "no" ] ; then rm -f -R $CACHEFOLDER ; fi } xdparser() { # parse args for x11docker # SHORT="hw:dxXayrz:scuomfvVep" LONG="help,wm:,desktop,command,x,X,xpra,Xpra,xephyr,Xephyr,sudo,root,clipboard,hostuser,sudouser,home,fullscreen,verbose,VERBOSE,exe,cid,cache,ps,resizeable,size:,starter" PARSED=`getopt --options $SHORT --longoptions $LONG --name "$0" -- "$@"` eval set -- "$PARSED" if [[ $? != 0 ]]; then error $? ; fi while [ -n "$1" ] ; do case $1 in -h|--help) usage && exit 0 # show help/usage and exit ;; -w|--wm) WINDOWMANAGER=$2 && shift # custom window manager ;; -d|--desktop) DESKTOPIMAGE="yes" # desktop environment or window manager in image; don't run own window manager ;; --command) ONLYSHOWCOMMAND="yes" # only show created docker command and exit ;; -s|--sudo) GETROOT="sudo $GETROOT" # use sudo to run 'su -c docker ...' ;; -x|-X|--x|--X) AUTOCHOOSESERVER="yes" # use xpra or Xephir instead of switchung to new tty with X (depending on option --desktop) # autodect best way to run ;; -a|--xpra|--Xpra) XSERVER="XPRA" # use Xpra instead of X ;; -y|--xephyr|--Xephyr) XSERVER="XEPHYR" # use Xephyr instead of X ;; -c|--clipboard) SHARECLIPBOARD="yes" # share host clipboard with dockered applications (Xpra only) ;; -f|--fullscreen) XEPHYRFULLSCREEN="yes" # fullscreen mode for Xephyr and ;; -z|--size) XEPHYRSIZE="-screen $2" && shift # set screen size for Xephyr ;; -r|--resizeable) XEPHYRARGS="$XEPHYRARGS -resizeable" # make Xephyr window resizeable ;; -e|--exe) X11DOCKERMODE="exe" # execute application from host instead of running docker image ;; --root) ALLOWROOT="yes" # allow x11docker to be run as root ;; -u|--hostuser) ADDHOSTUSER="yes" # create user similar to host user in created container ;; -o|--sudouser) ADDHOSTUSER="yes" # same as '--hostuser', but with root rights via sudo without password ADDSUDOUSER="yes" ;; -m|--home) ADDUSERHOME="yes" # share folder ~/x11docker/IMAGENAME with container ;; --starter) CREATEDESKTOPSTARTER="yes" # create desktop starter and exit ;; -p|--ps) PRESERVECONTAINER="yes" # presreve container instead of removing it with 'docker run --rm' ;; --cid) SHOWCIDFILENAME="yes" # show filename of file containing container ID ;; --cache) PRESERVECACHEFILES="yes" # don't remove cache files on exit ;; -v|--verbose) VERBOSE="verbose" # be verbose ;; -V|--VERBOSE) VERBOSE="VERBOSE" # be very verbose ;; --) shift # arguments to submit to docker (or host executable) if [ "$X11DOCKERMODE" != "exe" ] ; then X11DOCKERMODE="run" ; fi # will run image, if not already changed to '--exe' while [ -n "$1" ] ; do # get all options for docker after '--' DOCKERARGS="$DOCKERARGS $1" if [ "-" != "$(echo $1 | cut -c 1)" ] ; then # (ignore -options) WINDOWTITLE="$WINDOWTITLE""_$1" # create window title for Xephyr if [ -z "$IMAGENAME" ] ; then IMAGENAME="$1" ; fi # get name of image fi if [ -n "$WINDOWTITLE" ] ; then IMAGECOMMAND="$IMAGECOMMAND $1" ;fi # get image command without 'docker run' options if [ -z "$IMAGENAME" ] ; then DOCKEROPTIONS="$DOCKEROPTIONS $1" ; fi # options for 'docker run' without imagename/image args shift done if [ -n "$IMAGECOMMAND" ] ; then IMAGECOMMAND=`echo $IMAGECOMMAND | sed "s/$IMAGENAME//"` # remove image name from image command fi if [ -z "$(eval echo $DOCKERARGS)" ] ; then X11DOCKERMODE="onlyX" ; fi # if arguments are empty, then only run X server with window manager ;; *) error "error while parsing $1" # should never happen(tm) ;; esac shift done echo $DOCKEROPTIONS } configwindowmanager() { # set some window manager configs, if needed # # needs $WM_NAME to be set, will change $WINDOWMANAGER # case $WM_NAME in cinnamon|cinnamon-session) WINDOWMANAGER="cinnamon-session -f" WM_NAME="cinnamon" ;; compiz) # set minmal config to have useable window decoration and can move windows if [ ! -e "$HOME/.config/compiz-1/compizconfig/Default.ini" ] ; then mkdir -p "$HOME/.config/compiz-1/compizconfig" echo "[core] s0_active_plugins = core;composite;opengl;decor;resize;move; " > "$HOME/.config/compiz-1/compizconfig/Default.ini" fi ;; enlightenment|e17|e16) WINDOWMANAGER="enlightenment_start" WM_NAME="enlightenment" ;; gnome|gnome-shell|gnome-session) # crashes without option '--sm-disable' on ubuntu 14.04 WINDOWMANAGER="gnome-shell --sm-disable" WM_NAME="gnome-shell" ;; matchbox) WINDOWMANAGER="matchbox-window-manager" ;; mate|mate-session) WINDOWMANAGER="mate-session -f" WM_NAME="mate" ;; marco) WINDOWMANAGER="marco --sm-disable" if [ -z "`command -v mate-session`" ] ; then warning "Found window manager marco. It seems that not all dependencies are installed. Will try to search for another window manager" WINDOWMANAGER="lookfornewwindowmanager" fi ;; mate-wm) WINDOWMANAGER="marco --sm-disable" ;; muffin) WINDOWMANAGER="muffin --sm-disable" if [ -z "`command -v cinnamon`" ] ; then warning "Found window manager muffin. It seems that not all dependencies are installed. Will try to search for another window manager" WINDOWMANAGER="lookfornewwindowmanager" fi ;; tinywm) error "Window manager $WINDOWMANAGER is not working well, sorry. Please choose another window manager with option '-w, --wm'. x11docker will try to find another one on your system." WINDOWMANAGER="lookfornewwindowmanager" ;; openbox) # disable session management of openbox, otherwise it could stop your main session, too WINDOWMANAGER="openbox --sm-disable" ;; esac # # special check: normally Xephyr runs without window manager if [ "$XSERVER" = "XEPHYR" ] && [ "$XEPHYRFULLSCREEN" = "no" ] ; then case $WM_NAME in gnome-shell|cinnamon) warning "$WM_NAME can have problems in Xephyr, except running in fullscreen mode (option '-f, --fullscreen'). Will try to to find another window manager" WINDOWMANAGER="lookfornewwindowmanager" esac fi } { ######################## part: some init (parsing, checking permissions) ########################## ## trap # trap finish EXIT # SIGINT SIGTERM SIGHUP KILL QUIT ## parse x11dockerrc, if exists # if [ -e "$X11DOCKERRC" ] ; then xdparser $(tail -1 $X11DOCKERRC) ;fi ## parse command line options for x11docker # xdparser $X11DOCKERARGS ## check if script is run as root, if yes, show error & exit # if [ "$ALLOWROOT" = "no" ] ; then # option '-r,--root': if "yes", allow root to run x11docker if [ "0" = "$(id -u)" ] ; then error "x11docker should not run as root. Every child process of the new X server would get root privileges, too. To run docker, you will be prompted for your root password only for this one command. On systems without a root password, like Ubuntu, choose option '-s, --sudo' If you want to allow root to run x11docker, use option '-r, --root'" fi else if [ "0" = "$(id -u)" ] ; then GETROOT="" ; fi # if running as root and it is allowed, then diable password prompt fi ## if user is member of group docker, then avoid password prompt # if [ -n "`id | grep docker`" ] ; then GETROOT="" ; fi } { ######################## part: choose and check X server ########################################## ## option '-x, --X': show on display :0 instead of switching to another tty # check if either xpra or Xephir should be used for this job # if [ "$AUTOCHOOSESERVER" = "yes" ] ; then if [ "$DESKTOPIMAGE" = "no" ] ; then XSERVER="XPRA" # use xpra for single applications else XSERVER="XEPHYR" # use Xephyr for desktop environments fi fi ## check if choosed X server is installed # if [ "$XSERVER" = "XPRA" ] ; then # check Xpra command -v "/usr/bin/xpra" >/dev/null 2>&1 || { warning "could not find executable '/usr/bin/xpra'. Also needed: 'xvfb' Try 'apt-get install xpra xvfb' to install Xpra and Xvfb. Fallback: x11docker will try to use Xephyr" XSERVER="XEPHYR" } fi if [ "$XSERVER" = "XEPHYR" ] ; then # check Xephyr command -v "/usr/bin/Xephyr" >/dev/null 2>&1 || { warning "could not find executable '/usr/bin/Xephyr'. Try 'apt-get install xephyr' to install Xephyr. Fallback: x11docker will try to use X11" XSERVER="X11" } fi if [ "$XSERVER" = "XPRA" ] ; then # check Xvfb command -v "/usr/bin/Xvfb" >/dev/null 2>&1 || { warning "could not find executable '/usr/bin/Xvfb'. Try 'apt-get install xvfb' to install Xvfb. Need Xvfb to run Xpra. Fallback: x11docker will try to use X11" XSERVER="X11" } fi if [ "$XSERVER" = "X11" ] ; then # check X command -v "/usr/bin/X" >/dev/null 2>&1 || { error "Could not find executable '/usr/bin/X'. Try 'apt-get install xorg' to install X server." } # if x11docker is running from within an X server, check if user is allowed to run a new X server if [ -z "`cat /etc/X11/Xwrapper.config | grep "allowed_users=anybody"`" ] && [ -n "$STARTDISPLAY" ] ; then error "You are not allowed to start a new X server. To start a new X server from within an already running X session, you need to run 'dpkg-reconfigure x11-common' and choose option 'anybody'. Otherwise, you can switch to tty1 with and start x11docker there. (As a default configuration of X, only root or console users can start a new X server) Otherwise, you can use Xpra or Xephyr (see options '--xpra' and '--xephyr')." fi fi ## check for already running X server, if $XSERVER is not "X" # (check could be better, not only for standard :0) # if [ "$XSERVER" != "X11" ] && [ -z "$STARTDISPLAY" ] ; then if [ -S "/tmp/.X11-unix/X0" ] ; then STARTDISPLAY=":0" echo "NOTE: $XSERVER will show its content on display $STARTDISPLAY" else error "No X server found on display :0. You cannot run $XSERVER without an already running X server. Exiting." fi fi } { ######################## part: choose and check window manager #################################### if [ "$DESKTOPIMAGE" = "no" ] && [ ! "$XSERVER" = "XPRA" ] ; then # check only if a window manager is needed # ## try to find out current window manager to use it for x11docker (i.e., run a new instance of it) # if [ -n "$(command -v 'wmctrl')" ] ; then WMCTRL_IS_INSTALLED="yes" ; else WMCTRL_IS_INSTALLED="no" ; fi # if [ -z "$WINDOWMANAGER" ] && [ "$WMCTRL_IS_INSTALLED" = "yes" ] ; then # option '-w, --wm': maybe a window manager is already set WINDOWMANAGER=`wmctrl -m | grep "PID" | awk '{print $2}' ` # at first get pid if [ -e "/proc/$WINDOWMANAGER" ] ; then # check if pid is valid WINDOWMANAGER=`ls -l "/proc/$WINDOWMANAGER/exe" | awk '{print $11}'` # if yes, then get /path/executable else # otherwise, try unsecure way over name WINDOWMANAGER=`wmctrl -m | grep "Name" | awk '{print $2}' | tr '[:upper:]' '[:lower:]'` #| awk '{print $1}'` fi fi # if [ -n "$WINDOWMANAGER" ] ; then # get base name without options of window manager WM_NAME=$(echo `basename $WINDOWMANAGER` | awk '{print $1}') # check window manager, set configs, if needed configwindowmanager fi # ## if no executable window manager is found, then search for another one # if [ -z "$(command -v $WINDOWMANAGER)" ] ; then for ARGUMENT in $WM_ALL ; do WINDOWMANAGER=`command -v $ARGUMENT` if [ -n "$WINDOWMANAGER" ] ; then break ; fi done fi # ## again, get base name without options of window manager WM_NAME=$(echo `basename $WINDOWMANAGER` | awk '{print $1}') # again, check window manager, set configs, if needed configwindowmanager # ## check if window manager is executable now; if not, show error & exit # if [ -z "$(command -v $WINDOWMANAGER)" ] ; then error "could not find any executable window manager. Please choose a window manager with option '-w, --wm' Type 'x11docker --help' to get a list of recommended window managers." fi # else WINDOWMANAGER="" # if XPRA works as window manager, or image contains its own wm/DE, then x11docker doesn't need one fi } { ######################## part: check free display and configure cache ############################# ## create main cache folder and config folder, if not already present # mkdir -p $CACHEFOLDER mkdir -p $CONFIGFOLDER ## Look for next free display and change some display specific variables # case $XSERVER in # set initial value for searching X11) NEWDISPLAY="0" ;; # low number for X displays XPRA) NEWDISPLAY="100" ;; # high numbers fpr Xpra displays XEPHYR) NEWDISPLAY="200" ;; # higher number for Xephyr displays esac # while [ -e "/tmp/.X11-unix/X$NEWDISPLAY" ]; do # search unused X socket NEWDISPLAY=$(($NEWDISPLAY + 1)) done # CACHEFOLDER="$CACHEFOLDER/X$NEWDISPLAY" # set cache subfolder # XINITRC="$CACHEFOLDER/$XINITRC" XINITLOGFILE="$CACHEFOLDER/$XINITLOGFILE" XTERMRC="$CACHEFOLDER/$XTERMRC" XTERMLOGFILE="$CACHEFOLDER/$XTERMLOGFILE" DOCKERLOGFILE="$CACHEFOLDER/$DOCKERLOGFILE" DOCKERPIDFILE="$CACHEFOLDER/$DOCKERPIDFILE" XSERVERCOOKIE="$CACHEFOLDER/$XSERVERCOOKIE" XCLIENTCOOKIE="$CACHEFOLDER/$XCLIENTCOOKIE" XPRASERVERLOGFILE="$CACHEFOLDER/$XPRASERVERLOGFILE" XCOMMANDFILE="$CACHEFOLDER/$XCOMMANDFILE" VNCPASSWORDFILE="$CACHEFOLDER/$VNCPASSWORDFILE" VNCEXPECTRC="$CACHEFOLDER/$VNCEXPECTRC" VNCPORT="$(expr 5900 + $NEWDISPLAY)" CIDFILE="$CACHEFOLDER/$CIDFILE" BGPIDFILE="$CACHEFOLDER/$BGPIDFILE" CREATEUSERSCRIPT="$CACHEFOLDER/$CREATEUSERSCRIPT" #NEWXSOCKET="/tmp/.X11-unix/X$NEWDISPLAY" #NEWXLOCK="/tmp/.X$NEWDISPLAY-lock" # NEWDISPLAY=":$NEWDISPLAY" # finally: add ':' to $NEWDISPLAY ## Get inet Adress of docker0 interface and # set $TCPDISPLAY (display number for dockered applicaions, connected to X over tcp) # DOCKERIP=`ip -4 -o a | grep docker0 | awk '{print $4}' | cut -d/ -f1` TCPDISPLAY=$DOCKERIP$NEWDISPLAY ## create and clean cache subfolder # mkdir -p $CACHEFOLDER rm -f -R $CACHEFOLDER/* #if [ $! ] ; then # warning "Could not remove $CACHEFOLDER. # Sometimes, when the X server is terminated faster than the docker # process running inside it, docker creates a folder as root to # replace missing shared files. This is a known bug, docker should # not do that. As x11docker is not running as root, it cannot delete # it. Please type in your password to remove this folder. # Otherwise, press [CTRL][C] to stop x11docker. # $GETROOT 'rm -R -i $CACHEFOLDER/*'" # $GETROOT "rm -R -i $CACHEFOLDER/*" #fi # here should be a check for "zombie" folders that cannot be deleted } { ######################## part: create command to run docker ####################################### ## create docker command # case $X11DOCKERMODE in run) COMMAND='docker run' COMMAND="$COMMAND -e DISPLAY=$TCPDISPLAY" # display over tcp COMMAND="$COMMAND -v $XCLIENTCOOKIE:/Xcookie:ro -e XAUTHORITY=/Xcookie" # share new X authentication cookie file COMMAND="$COMMAND --cidfile=$CIDFILE" # write container id in file $CIDFILE ;; exe|onlyX) COMMAND="# dontrundocker" ;; esac # ## option '-p, --ps': preserve container yes/no # if [ "$PRESERVECONTAINER" = "no" ] ; then COMMAND="$COMMAND --rm" ; fi # ## option '--home': share folder ~/.local/share/x11docker/IMAGENAME with created container as its home directory # if [ "$ADDUSERHOME" = "yes" ] ; then # define path to shared folder and remove '/' from image name ADDUSERHOMEFOLDER=$HOME/.local/share/x11docker/`echo $IMAGENAME | tr / -` mkdir -p $ADDUSERHOMEFOLDER COMMAND="$COMMAND -v $ADDUSERHOMEFOLDER:/home/$BENUTZER" fi # ## option '-u, --hostuser' : create container user similar to host user # if [ "$ADDHOSTUSER" = "yes" ] ; then # # CMD in image will be overwritten by createuser script. If none is defined on CLI, get CMD from image # ('docker inspect' needs root permissions. That's not nice) # if [ -z "$IMAGECOMMAND" ] ; then verbose "No command for image specified. Need to look for CMD command of docker image. Please type in your password to run $GETROOT 'docker inspect --format='{{.Config.Cmd}}' $IMAGENAME'" IMAGECOMMAND=`$GETROOT "docker inspect --format='{{.Config.Cmd}}' $IMAGENAME"` IMAGECOMMAND=$(echo $IMAGECOMMAND | tr -d []) IMAGECOMMAND=$(echo $IMAGECOMMAND | sed 's/\/bin\/sh -c//') verbose "To avoid this password prompt, you can start x11docker next time with command: x11docker $X11DOCKERARGS $IMAGECOMMAND" X11DOCKERARGS="$X11DOCKERARGS $IMAGECOMMAND" fi # # create script createuser. It will also start the image with given (or extracted) command # echo "#! /bin/bash" > $CREATEUSERSCRIPT # create user echo "addgroup --gid `id -g $BENUTZER` $BENUTZER" >> $CREATEUSERSCRIPT echo "adduser --disabled-password --uid `id -u` --gid `id -g` --gecos " '""' " --home /home/$BENUTZER $BENUTZER" >> $CREATEUSERSCRIPT echo "chown $BENUTZER:$BENUTZER /home/$BENUTZER" >> $CREATEUSERSCRIPT echo "HOME=/home/$BENUTZER" >> $CREATEUSERSCRIPT # option '--sudouser': add sudo permissions without password if [ "$ADDSUDOUSER" = "yes" ] ; then echo 'echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers' >> $CREATEUSERSCRIPT echo "adduser $BENUTZER sudo" >> $CREATEUSERSCRIPT fi # run image command as user: echo "su $BENUTZER -c '$IMAGECOMMAND'" >> $CREATEUSERSCRIPT # # include created script in container COMMAND="$COMMAND -v $CREATEUSERSCRIPT:/createuser" # use created script as command (CMD) to run image DOCKERARGS="$DOCKEROPTIONS $IMAGENAME /bin/bash /createuser" fi # # add custom docker commands # COMMAND="$COMMAND $DOCKERARGS" ## option '-c, --command': only show created docker command and exit # if [ "$ONLYSHOWCOMMAND" = "yes" ] ; then echo $COMMAND && exit 0 ; fi ## option '--cid': show filename of file that will contain container id # if [ "$SHOWCIDFILENAME" = "yes" ] ; then echo $CIDFILE ; fi } { ######################## part: create command to start X server ################################### case $XSERVER in X11) XCOMMAND="/usr/bin/X $NEWDISPLAY $X11ARGS -auth $XSERVERCOOKIE" ;; XPRA) # To use Xpra, Xvfb will run first. Xpra server doesn't allow tcp connections, even if it is told to do it. case $SHARECLIPBOARD in # option '-c, --clipboard' yes) XPRAARGS="$XPRAARGS --clipboard" ;; no) XPRAARGS="$XPRAARGS --no-clipboard" ;; esac # screen size could be checked better - see xpra/xvfb/xdummy sites about this XCOMMAND="/usr/bin/Xvfb $NEWDISPLAY $XVFBARGS -screen 0 16384x8192x24+32 -auth $XSERVERCOOKIE" ;; XEPHYR) case $XEPHYRFULLSCREEN in # option '--fullscreen' yes) XEPHYRARGS="$XEPHYRARGS -fullscreen" ;; no) XEPHYRARGS="$XEPHYRARGS $XEPHYRSIZE" ;; esac XEPHYRARGS="$XEPHYRARGS -title XEPHYR$WINDOWTITLE" # set window title XCOMMAND="/usr/bin/Xephyr $NEWDISPLAY $XEPHYRARGS -auth $XSERVERCOOKIE" ;; esac } { ######################## part: create xinitrc ##################################################### echo "#! /bin/bash" > $XINITRC # # subroutine to store PIDs and process names in file $BGPIDFILE # echo 'storepid () { echo $1 $2 >> '$BGPIDFILE' ; }' >> $XINITRC # # things to be done on display :0 : # # set X variables to :0 echo "export DISPLAY=$STARTDISPLAY" >> $XINITRC echo "export XAUTHORITY=$STARTXAUTHORITY" >> $XINITRC case $XSERVER in XEPHYR) echo "setxkbmap -display $STARTDISPLAY -print | xkbcomp - $NEWDISPLAY" >> $XINITRC ;; esac # # things to be done on new display: # # set X variables to new display echo "export DISPLAY=$NEWDISPLAY" >> $XINITRC echo "export XAUTHORITY=$XCLIENTCOOKIE" >> $XINITRC # # create background color. #7F7F7F is color of xfwm4 background echo 'xsetroot -solid "#7F7F7F"' >> $XINITRC # # create new XAUTHORITY cookie file echo 'echo "" > '$XCLIENTCOOKIE >> $XINITRC echo "xauth generate $NEWDISPLAY . untrusted" >> $XINITRC echo "xauth add $TCPDISPLAY . "'`mcookie`' >> $XINITRC echo "cp $XCLIENTCOOKIE $XSERVERCOOKIE" >> $XINITRC # # start window manager, if defined if [ -n "$WINDOWMANAGER" ] ; then echo "$WINDOWMANAGER &" >> $XINITRC echo 'WINDOWMANAGERPID=$!' >> $XINITRC echo 'storepid $! '$WM_NAME >> $XINITRC # (adding pid to $XINITJOBSTOKILL later) fi # case $XSERVER in XPRA) # run xpra server and xpra viewer # start Xpra server on display created by Xvfb echo "xpra start $NEWDISPLAY --use-display --no-daemon $XPRAARGS >$XPRASERVERLOGFILE 2>&1 &" >> $XINITRC echo 'XINITJOBSTOKILL="$XINITJOBSTOKILL $!"' >> $XINITRC echo 'storepid $! xpra' >> $XINITRC # wait for Xpra server to be ready echo 'ZEIT=`date +%s`' >> $XINITRC echo 'while [ -z "$(cat '$XPRASERVERLOGFILE' | grep '"'xpra is ready'"')" ] ; do' >> $XINITRC echo ' echo "waiting for xpra server to be ready..." && sleep 0.2 ' >> $XINITRC echo ' COUNT=$(echo "`date +%s` - $ZEIT" | bc)' >> $XINITRC echo ' if [ 5 -lt $COUNT ] ; then' >> $XINITRC echo ' echo "x11docker: Timeout error: Xpra server not ready after 5 seconds. Exiting."' >> $XINITRC echo ' exit 1' >> $XINITRC echo ' fi' >> $XINITRC echo 'done' >> $XINITRC # check for X error echo 'if [ -n "$(cat '$XPRASERVERLOGFILE" | grep 'X Window System error')"'" ] ; then' >> $XINITRC echo ' echo "x11docker: Error: received an X Window System error. See '$XPRASERVERLOGFILE' for details. Exiting"' >> $XINITRC echo ' exit 1' >> $XINITRC echo 'fi' >> $XINITRC # set X variables to display :0 echo "export DISPLAY=$STARTDISPLAY" >> $XINITRC echo "export XAUTHORITY=$STARTXAUTHORITY" >> $XINITRC # start xpra viewer on display :0 echo "xpra attach $NEWDISPLAY &" >> $XINITRC echo 'XINITJOBSTOKILL="$XINITJOBSTOKILL $!"' >> $XINITRC echo 'storepid $! xpra' >> $XINITRC # set X variables back to new display echo "export DISPLAY=$NEWDISPLAY" >> $XINITRC echo "export XAUTHORITY=$XCLIENTCOOKIE" >> $XINITRC ;; esac # # choose apps to run and wait for them to exit case $X11DOCKERMODE in run) # password for docker will be prompted in xterm echo "xterm -l -lf $XTERMLOGFILE -title x11docker -e '/bin/bash $XTERMRC'" >> $XINITRC echo "if [ -e $DOCKERPIDFILE ] ; then" >> $XINITRC echo ' DOCKERPID=`cat '$DOCKERPIDFILE'`' >> $XINITRC echo ' storepid $DOCKERPID docker' >> $XINITRC echo ' while [ -n "$(ps -A | grep $DOCKERPID | grep docker)" ] ; do' >> $XINITRC echo ' sleep 0.4' >> $XINITRC echo ' done' >> $XINITRC echo 'fi' >> $XINITRC ;; exe) # run host application (window manager is already running) echo "$DOCKERARGS &" >> $XINITRC echo 'storepid $! '`echo $DOCKERARGS | awk '{print $1}'` >> $XINITRC echo 'wait $!' >> $XINITRC ;; onlyX) # wait for window manager to exit echo 'wait $WINDOWMANAGERPID' >> $XINITRC echo '$WINDOWMANAGERPID=""' >> $XINITRC ;; esac # # clean up background jobs # ($WINDOWMANAGERPID can already be empty, see case onlyX above. maybe window manager wasn't running at all) echo 'XINITJOBSTOKILL="$XINITJOBSTOKILL $WINDOWMANAGERPID"' >> $XINITRC echo 'for ARGUMENT in $XINITJOBSTOKILL ; do kill $ARGUMENT ; done' >> $XINITRC } { ######################## part: create xtermrc (used by xinitrc to get password and run docker) #### if [ "$X11DOCKERMODE" = "run" ] ; then echo "#! /bin/bash" > $XTERMRC if [ "$WINDOWMANAGER" = "" ] && [ "$XSERVER" != "XPRA" ] ; then echo "echo 'Please move mouse cursor into terminal area to get keyboard focus.'" >> $XTERMRC fi echo "echo 'x11docker will start docker on display $NEWDISPLAY with command:'" >> $XTERMRC echo "echo $GETROOT '$COMMAND'" >> $XTERMRC echo "echo 'Please type in your password:'" >> $XTERMRC echo "$GETROOT 'nohup $COMMAND > $DOCKERLOGFILE 2>&1 & echo "'$!'" > $DOCKERPIDFILE'" >> $XTERMRC fi } { ######################## part: options '-v, --verbose', '-V, --VERBOSE': be [very] verbose ####### if [ -n "$VERBOSE" ] ; then verbose "Found free display $NEWDISPLAY" verbose "As X server will be used: $XSERVER" verbose "IP of docker interface is $DOCKERIP As display number for the container $TCPDISPLAY will be used." if [ "$ADDUSERHOME" = "yes" ] ; then verbose "Sharing folder $ADDUSERHOMEFOLDER with container as its home directory $HOME/$BENUTZER" ; fi verbose "Created docker command: $COMMAND" verbose "Created X server command: $XCOMMAND" verbose "Will run xinit with command xinit $XINITRC -- $XCOMMAND > $XINITLOGFILE 2>&1" # # some infos about window manager # if [ "$WMCTRLINSTALLED" = "no" ] ; then warning "To autodetect your current window manager, x11docker needs package 'wmctrl' to be installed. Please consider to install 'wmctrl' with command: 'apt-get install wmctrl'" fi # verbose "found window manager $WINDOWMANAGER" case $WM_NAME in cinnamon) warning "Found cinnamon as window manager. Please don't use shutdown option of cinnamon, because it would shut down your main session, too." ;; compiz) verbose "compiz is a good choice for use as windowmanager in x11docker. If not already present, x11docker creates a minimal basic configuration for compiz. Run x11docker with command 'x11docker --wm compiz --exe ccsm' to be able to enable more features of compiz. If not already done, install ccsm with 'apt-get install compizconfig-settings-manager' Consider to use a more lightweight window manager with option '-w, --wm'" ;; kwin|enlightenment) verbose "As window manager $WINDOWMANAGER was found. This window manager works fine in x11docker, but is a bit heavy in memory and cpu usage. You could choose a smaller one with option '-w, --wm". ;; gnome-shell) warning "gnome-shell is possible, but not recommended for use in x11docker. Especially, it can crash if used within Xephyr. Consider to use another window manager with option '-w, --wm'. In your case, 'mutter' would be a good choice. You can install 'mutter' with 'apt-get install mutter'." ;; icewm) warning "IceWM is a good window manager. For use in x11docker, it has one drawback: You should not use the logout option of IceWM, because it will try to close your main session, too. Consider to use another window manager with option '-w, --wm'" ;; lxsession) verbose "lxsession is a good choice to have a comfortable window management and a usefull desktop environment in x11docker. Please don't use shutdown option of lxsession, because it would shut down your main session, too." ;; openbox) verbose "Window manager openbox is a good choice, as it is small, fast and comfortable. It has one small issue: The terminal window to type in your password doesn't raise on itself; you have to click on the screen to see the window and click it again to give it focus. Consider to use another window manager with option '-w, --wm'" esac # verbose "Created xinitrc: $(cat $XINITRC)" verbose "Created xtermrc: $(cat $XTERMRC)" # # Logfiles # verbose "Logfiles are: $XINITLOGFILE $XPRASERVERLOGFILE $XTERMLOGFILE $DOCKERLOGFILE" fi # if very verbose, then show logfiles live on stdout if [ "$VERBOSE" = "VERBOSE" ] ; then tail -F $XINITLOGFILE $XTERMLOGFILE $DOCKERLOGFILE $XPRALOGFILE 2> /dev/null & PIDSTOKILL="$PIDSTOKILL $!" storepid $! tail fi } { ######################## part: option '--starter': create desktop starter & exit if [ "$CREATEDESKTOPSTARTER" = "yes" ] ; then X11DOCKERARGS=$(echo $X11DOCKERARGS | sed 's/--starter/ /') echo "#!/usr/bin/xdg-open [Desktop Entry] Type=Application Name=`echo $IMAGENAME | tr / -` $IMAGECOMMAND x11docker Exec=$0 $X11DOCKERARGS Icon=system-run Comment= Categories=System Keywords=` # echo $IMAGENAME | tr / -` $IMAGECOMMAND x11docker docker TryExec=x11docker $X11DOCKERARGS " > "`xdg-user-dir DESKTOP`/`echo $IMAGENAME | tr / -`x11docker.desktop" verbose "Created desktop icon `xdg-user-dir DESKTOP`/`echo $IMAGENAME | tr / -`x11docker.desktop" exit 0 fi } ######################## Finally: run new X server ################################################## xinit $XINITRC -- $XCOMMAND > $XINITLOGFILE 2>&1 exit 0 # trap EXIT will start finish()