When Linux and Windows Can Talk
Let's say I'm in an Ubuntu WSL2 session and I want to copy a file to
Google Drive. The latest version of Google Drive allows access via
a mounted drive, in my case,
G:\.
The simplest way I've found to copy a file into G:\ is to
make use of PowerShell:
$ date > data.txt
$ powershell.exe -C copy "c:/Users/benji/Downloads/data.txt" "'G:\My Drive\'"
$ powershell.exe -C dir "'G:\My Drive\data.txt'"
Directory: G:\My Drive
Mode LastWriteTime Length Name
---- ------------- ------ ----
------ 2/14/2022 6:13 AM 29 data.txt
$ powershell.exe -C type "'G:\My Drive\data.txt'"
Mon Feb 14 06:13:56 EST 2022
$ cat data.txt
Mon Feb 14 06:13:56 EST 2022
$
While the commands above are clunky, they're straightfoward. I'm
kicking off an instance of PowerShell from Linux
to run simple Windows commands. Because those commands are running
under Windows, they can trivially access G:.
Or, consider another example. Let's say I want to open a web page
from emacs running under Ubuntu. In emacs, I
evaluate the following lisp code:
(browse-url "https://blogbyben.com")
The result is that the URL is opened in the Windows version of
Chrome. I didn't have configure this behavior, it Just Works. Until it doesn't.
When Linux and Windows Aren't On Speaking Terms
Every once in a while, I'll use the code above
and I'll be greeted with a long delay and finally an error
message. For example:
$ powershell.exe -C type "'G:\My Drive\data.txt'"
<3>init: (26039) ERROR: UtilAcceptVsock:244: accept4 failed 110
In emacs, running browse-url will occasionally
just not work. Digging into the code, I realized that
emacs is running
xdg-open. Running this command manually, when the integration is
broken, results in a similar error message.
$ time xdg-open https://www.blogbyben.com
<3>init: (26536) ERROR: UtilAcceptVsock:244: accept4 failed 110
<3>init: (26546) ERROR: UtilAcceptVsock:244: accept4 failed 110
<3>init: (26548) ERROR: UtilAcceptVsock:244: accept4 failed 110
<3>init: (26550) ERROR: UtilAcceptVsock:244: accept4 failed 110
/bin/xdg-open: 869: firefox: not found
/bin/xdg-open: 869: iceweasel: not found
/bin/xdg-open: 869: seamonkey: not found
/bin/xdg-open: 869: mozilla: not found
/bin/xdg-open: 869: epiphany: not found
/bin/xdg-open: 869: konqueror: not found
/bin/xdg-open: 869: chromium: not found
/bin/xdg-open: 869: chromium-browser: not found
/bin/xdg-open: 869: google-chrome: not found
<3>init: (26599) ERROR: UtilAcceptVsock:244: accept4 failed 110
<3>init: (26609) ERROR: UtilAcceptVsock:244: accept4 failed 110
<3>init: (26611) ERROR: UtilAcceptVsock:244: accept4 failed 110
<3>init: (26613) ERROR: UtilAcceptVsock:244: accept4 failed 110
/bin/xdg-open: 869: links2: not found
/bin/xdg-open: 869: elinks: not found
/bin/xdg-open: 869: links: not found
/bin/xdg-open: 869: lynx: not found
/bin/xdg-open: 869: w3m: not found
xdg-open: no method available for opening 'https://www.blogbyben.com'
real 1m20.400s
user 0m0.159s
sys 0m0.067s
What the Heck?
From poking around the web, I learned that the
integration between
Windows and WSL2 happens via the environment variable
$WSL_INTEROP. This variable is set to a file system socket
which provides the communication channel.
When I look in /var/run/WSL I see a number of files:
$ ls -1 /var/run/WSL/
10088_interop
11670_interop
13888_interop
1663_interop
17081_interop
1_interop
2189_interop
6106_interop
6268_interop
8855_interop
8_interop
The trick: WSL_INTEROP needs to be set to the right one for
proper communication to happen. I confirmed this by going
through the list of files, setting each one to WSL_INTEROP
and seeing if that fixed the problem. Eventually it did. But trial
and error is hardly the way I want to get back on track once Linux
and Windows stop talking.
This discussion
thread suggests using pstree to
automatically set WSL_INTEROP. However the recipe provided
didn't work for me.
The goal of the command appears to be to set
WSL_INTEROP to the value
/var/run/WSL/<PID>_interop where PID is the
process ID of the parent's init process.
Looking at my running system, I see that init is
running multiple times, and that the PIDs correspond to files in
/var/run/WSL.
$ ps auxwww|grep init
root 1 0.0 0.0 1772 1100 ? Sl Feb09 0:00 /init
root 8 0.0 0.0 1752 80 ? S Feb09 0:00 /init
root 8854 0.0 0.0 1772 100 ? Ss Feb10 0:00 /init
root 8855 0.0 0.0 1780 100 ? S Feb10 0:01 /init
ben 26051 0.0 0.0 8160 2488 pts/6 S+ 06:18 0:00 grep init
After a few attempts, I realized one way to get the correct init PID
is to use the last one in the process list. Using
grep, tail and awk I can extract this
value.
$ ps auxwww|grep init | grep -v grep | tail -1
root 8855 0.0 0.0 1780 100 ? S Feb10 0:02 /init
$ ps auxwww|grep init | grep -v grep | tail -1 | awk '{print $2}'
8855
I then added the following to my .bashrc:
$ export WSL_INTEROP=/var/run/WSL/$(ps auxwwww|grep init | \
grep -v grep | tail -1 | awk '{print $2}')_interop
This code properly sets WSL_INTEROP and revives the
Windows / Linux communication channel. With that in place,
I'm back to opening up web
pages and PowerShell'ing like a champ.