rsshx11-forwardingxquartz

How to recover X11 connection without exiting R on remote server


Short version: How do you restart a lost X11 connection from within R when working on a remote machine ?

Long version:

  1. The setup

I work with Mac 11.1 and connect to a remote Linux server to code in R (through Nvim-R). The steps I use are usually:

ssh -Y login@remote.server
screen -r my_R_work_session
conda activate my_environment
nvim code.r

and then type \rf command to open a R terminal within nvim.

Then I am able to plot anything in R (eg. with plot(iris)). The plot pops up in an external window (through X11 / XQuartz) on my Mac. It usually works fine at the beginning.

  1. The problem

But at some point, I might lose the connection to the server for just a second (internet failure, vpn stops...), and everything seems to work fine when I get back the connection to the server (main ssh connection is maintained) except the X11 connection.

At this point, the output of any plot(iris) would be:

Error in .External2(C_X11, d$display, d$width, d$height, d$pointsize,  :
  unable to start device X11cairo
In addition: Warning message:
In function (display = "", width, height, pointsize, gamma, bg,  :
  unable to open connection to X11 display ''
  1. The solution (but looking for something better!)

The only solution I found for the moment is starting a new ssh connection to the server and re-do the whole process up to starting the R session and plotting stuff. Sometimes, closing only the screen session works, but I still need to reload conda and my R session. But it's time-consuming (and annoying) and my guess is that there might be a better solution.

  1. What I have also tried (but doesn't change anything)

I found many posts googling this error, but still nothing that could resolve this issue.

- Check capabilities()

Before losing X11 connection, the output of capabilities() is:

       jpeg         png        tiff       tcltk         X11        aqua
       TRUE        TRUE        TRUE        TRUE        TRUE       FALSE
   http/ftp     sockets      libxml        fifo      cledit       iconv
       TRUE        TRUE        TRUE        TRUE        TRUE        TRUE
        NLS     profmem       cairo         ICU long.double     libcurl
       TRUE        TRUE        TRUE        TRUE        TRUE        TRUE

After losing X11 connection, the only difference is then that X11 is set to FALSE.

- Check DISPLAY

Refering to this post, I checked DISPLAY but everything seems to be fine. In terminal (before starting R or while r is running):

> echo $DISPLAY
localhost:10.0

In R:

> Sys.getenv("DISPLAY")
localhost:10.0

And I tried setting Sys.setenv("DISPLAY"=":0.0") but does not make any difference.

- X11UseLocalhost

This post suggests changing X11UseLocalhost no to X11UseLocalhost yes in /etc/ssh/sshd_config, but it does not make any difference to me.

- Xpra

Finally, I tried using Xpra following the suggestions from this post (probably the most promising one) but did not make any difference either. Although here I am not sure if I use it properly. Here is what I did:

[local] ssh -Y login@remote.server
[remote] screen -r xpra
[remote] xpra start :10
# exit screen
[local] xpra attach ssh:remote.server:10 
[remote] screen -r my_R_work_session
[remote] conda activate my_environment
[remote] nvim code.r

Thanks a lot for any comment and help !


Solution

  • For anyone bumping into this post, I ended up finding a super simple solution: you just need to start a new ssh -Y connection in parallel of your current session. Then you can redirect the DISPLAY parameter in the current session to the value in the new connection.

    Here is a more detailed example. In the current session (before starting the new ssh connection), if you try in R terminal :

    > capabilites()             # returns X11 = FALSE 
    > Sys.getenv('DISPLAY')     # returns localhost:xx.0
    localhost:12.0
    > x11('localhost:12.0')     # returns error message
    

    Now you start the new ssh connection in parallel: ssh -Y login@remote.server. You need to find the value of DISPLAY in this new session, eg:

    In bash:

    $ echo $DISPLAY
    localhost:13.0
    

    Or in R:

    > Sys.getenv('DISPLAY')
    localhost:13.0
    

    Finally, in the original R terminal, set the DISPLAY parameter to the new connection value:

    > Sys.setenv('DISPLAY' = 'localhost:13.0')
    > x11('localhost:13.0')  # or x11() - both should work
    

    There is also another option, is to modify the value of ForwardX11Timeout in ssh config file (~/.ssh/config). It is usually set to ~20 min by default (see man ssh). You can try set this to one day (or more), eg: ForwardX11Timeout 1d. This will prevent X11 to stop during any work session if it's opened less than 24 hours. You can even set ForwardX11Trusted yes in this config file. However, these options can lead to security leaks, so they need to be considered with care.