8

In order to perform some JavaScript unit tests with karma inside a docker container (based on ubuntu 14.04) I'm starting firefox in the container using a karma-script-launcher with xvfb-run. The start script looks like this:

#!/bin/bash
set -o errexit 

# nasty workaround as xvfb-run doesn't cleanup properly...
trap "pkill -f /usr/lib/firefox/firefox" EXIT

xvfb-run --auto-servernum --server-args='-screen 0, 1024x768x16' firefox $1

Starting the browser and executing the unit tests works very well. After executing the tests karma terminates the spawned browser instance - in my case the script that launched firefox over xvfb-run.

In the above script you can see that I registered a trap to kill the launched firefox on exit of my script. This works, but the script is not a very nice citizen as it terminates all instances of firefox that are currently running instead of just terminating the one instance that was launched by the script. I first tried to kill the xfvb-run process but killing this process has no effect on the sub-process launched by the xvfb-run script...

If I start firefox over xvfb-run manually there is a bunch of spawned processes:

root@1d7a5988e521:/data# xvfb-run --auto-servernum --server-args='-screen 0, 1024x768x16' firefox &
[1] 348
root@1d7a5988e521:/data# ps ax
  PID TTY      STAT   TIME COMMAND
    1 ?        Ss     0:00 bash
  348 ?        S      0:00 /bin/sh /usr/bin/xvfb-run --auto-servernum --server-args=-screen 0, 1024x768x16 firefox
  360 ?        S      0:00 Xvfb :99 -screen 0, 1024x768x16 -nolisten tcp -auth /tmp/xvfb-run.bgMEuq/Xauthority
  361 ?        Sl     0:00 /usr/lib/firefox/firefox
  378 ?        S      0:00 dbus-launch --autolaunch bcf665e095759bae9fc1929b57455cad --binary-syntax --close-stderr
  379 ?        Ss     0:00 //bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
  388 ?        S      0:00 /usr/lib/x86_64-linux-gnu/gconf/gconfd-2
  414 ?        R+     0:00 ps ax
root@1d7a5988e521:/data#

If I now kill the xvfb-run process (PID 348), only this process will be terminated, leaving the other processes running. If I kill the firefox process (PID 361) instead, the xvfb-run script correctly terminates and kills the other processes as well. But from my script I only know the PID of the xvfb-run process...

During my research I stumbled across this rather old bug report for xvfb-run which still seems to be valid in spite of the bug's status beeing fixed back in 2012.

Is there any polite way to terminate the xvfb-run process in order for the other processes to be cleaned up correctly?


I already asked this on Stack Overflow, but got no answer till now. Perhaps it's somewhat OT for Stack Overflow but better located here?!

4
  • do you need any of the features of xvfb-run? why not run Xvfb :1 -screen 0 1024x768x16 & and DISPLAY=:1 firefox & then you have their pids and can kill -15 the Xvfb which will close firefox too.
    – meuh
    Commented Jun 24, 2016 at 19:42
  • Currently I'm kind of relying on the auto-servernum feature of xvfb-run. But in my very special case I could hard code the display to use in the start script as I know that there is only one browser open at a time... However I was hoping to find a solution for the xvfb-run problem as I have the feeling that the above assumption (one browser at a time) will not last forever.
    – dpr
    Commented Jun 25, 2016 at 18:47
  • xvfb-run is just a shell script. it has a simple loop to discover a free display number that you could copy and reuse.
    – meuh
    Commented Jun 25, 2016 at 18:54
  • Yes, C&P not really my desired solution. But it currently looks like this might actually be my best option.
    – dpr
    Commented Jun 25, 2016 at 18:57

3 Answers 3

7

It sounds like you are only using xvfb-run for its --auto-servernum functionality.

As @meuh pointed out: that logic is actually pretty simple:

# Copyright (C) 2005 The T2 SDE Project
# Copyright (C) XXXX - 2005 Debian
# GNU GPLv2
find_free_servernum() {
    # Sadly, the "local" keyword is not POSIX.  Leave the next line commented in
    # the hope Debian Policy eventually changes to allow it in /bin/sh scripts
    # anyway.
    #local i

    i=$SERVERNUM
    while [ -f /tmp/.X$i-lock ]; do
        i=$(($i + 1))
    done
    echo $i
}

With that function defined: you could try an invocation like this instead of using xvfb-run:

Xvfb :$(find_free_servernum) -screen 0, 1024x768x16 firefox $1 &
THE_PID=$!
# kill Xvfb whenever you feel like it
kill -15 $THE_PID

With xvfb-run removed: we no longer need to worry about how to kill xvfb-run.

1
  • Upvoted answer, but not going to accept it as the question was how to properly terminate xvfb. This however doesn't seem to be possible and your workaround (same as the comment by @meuh) seems to be the best option...
    – dpr
    Commented Sep 26, 2016 at 10:10
1

I've run in to this very problem today.

All Xservers support the -terminate argument.

−terminate

causes the server to terminate at server reset, instead of continuing to run. This overrides a previous −noreset command line option.

So I ended up specifying this using xvfb-run's -s:

xvfb-run -d -s '-terminate' firefox --no-remote --profile $PROFILE_DIR $URL

So in our case of a Karma test runner, once the firefox instance is closed by Karma, the Xvfb server terminates itself as its last xclient (firefox) has terminated. xvfb-run completed execution after firefox exits too.

Edit

I ended up putting my own "simpler" version of the xvfb-run script together:


PROFILE=$(mktemp -d)
trap "rm -rf $PROFILE" EXIT

# Start Xvfb and let it find a display number itself.
# Some versions of Xvfb (apparently 1.17.x) refuse to write to stdout
Xvfb -displayfd 4 -terminate -nolisten tcp 4>$PROFILE/.Xdisplay &

# Wait a few seconds for Xvfb to start
sleep ${START_WAIT-2}

${FIREFOX_HOME}/firefox --profile $PROFILE --no-remote --display :$(<$PROFILE/.Xdisplay) "${@?}" &
# Karma appears to send a SIGTERM to the browser when it's done, forward that signal to the Firefox process.
trap "kill -SIGTERM $!" SIGTERM

# Wait for all children to terminate
wait

0

My solution was to start xvfb in Docker entrypoint script:

Xvfb :0 -screen 0 1024x768x24 &

You can put this same line also to some kind of OS start script (e.g. .bashrc) script if you are not using Docker.

Then in another script I exported the display and called the actual function I wanted to use

export DISPLAY=:0
firefox $1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .