Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to tell in Linux under WSL if using WSL1 vs WSL2 ? #4555

Closed
k7aay opened this issue Sep 30, 2019 · 37 comments
Closed

How to tell in Linux under WSL if using WSL1 vs WSL2 ? #4555

k7aay opened this issue Sep 30, 2019 · 37 comments
Labels

Comments

@k7aay
Copy link

k7aay commented Sep 30, 2019

Documentation missing instructions in how to tell within Linux under WSL whether using WSL1 or WSL2.

@bytebounder
Copy link

bytebounder commented Oct 1, 2019

Here is a helpful doc - https://docs.microsoft.com/en-us/windows/wsl/wsl2-install

while setting a distro to be backed by WSL 2, I would run the following command in Powershell:

wsl -s 2

You can find your Distro by running: wsl -l

@therealkenc
Copy link
Collaborator

You can find your Distro by running: wsl -l

Yes, but there is no particularly good way right now to know your own distribution name once you are inside. There was another ask for that recently #4479.

Probably (possibly?) one way is to run a regex on /proc/version. So WSL1 looks like:

Linux version 4.4.0-18985-Microsoft (Microsoft@Microsoft.com) (gcc version 5.4.0 (GCC) ) #1-Microsoft Fri Sep 13 14:26:00 PST 2019

While WSL2:

Linux version 4.19.67-microsoft-standard (oe-user@oe-host) (gcc version 8.2.0 (GCC)) #1 SMP Sun Aug 18 13:37:54 UTC 2019

If it were me I wouldn't make a hard bet on the WSL2 string being set in concrete, but conversely, I can't imagine the WSL1 format changing gratuitously (not without grumbling, anyway). Matching that "gcc version n.x.x" and testing for n < 8 probably pretty future proof. There are other heuristics that would work. Calling dmesg and looking for a stable string like "Hypervisor" (or pick another).

@Biswa96
Copy link

Biswa96 commented Oct 1, 2019

Here is a sample C program on how /init takes care of WSL1 vs WSL2:

#include <stdio.h>
#include <string.h>
#include <sys/utsname.h>

int main(void)
{
    struct utsname buf;
    memset(&buf, 0, sizeof buf);

    int ret = uname(&buf);
    if (ret == 0)
    {
        if (strstr(buf.release, "Microsoft"))
            printf("WSL1\n");
        else
            printf("WSL2\n");
    }
    else
        printf("uname error\n");

}

@therealkenc
Copy link
Collaborator

+1 for code always. But implied in my post was maybe better not to rely on oe-user@oe-host never ever changing. That strstr() is leaning pretty hard on a capital letter 'M'. [The string "icrosoft" matches both.]

@Biswa96
Copy link

Biswa96 commented Oct 1, 2019

There may be hundreds of ad-hoc way to do it. For example, only WSL2 has $WSL_INTEROP, /proc/config.gz, /dev/vsock, /dev/sda, tty shows /dev/pts etc.

@therealkenc
Copy link
Collaborator

therealkenc commented Oct 1, 2019

Money ➡️ mouth:

// g++ -std=c++17 wslver.cpp -o wslver
#include <fstream>
#include <iostream>
#include <array>
#include <string>
#include <charconv>
#include <regex>
using namespace std::string_literals;

int main(int argc, const char *argv[])
{
    std::ifstream ifs;
    ifs.open("/proc/version");
    constexpr size_t bufsz = 128;
    std::array<char, bufsz> buf;
    ifs.getline(buf.data(), bufsz, '\n');
    std::string proc_ver(buf.data(), buf.size());
    std::regex rx("Linux version [0-9]+\\.[0-9]+\\.[0-9]+.*[Mm]icrosoft.*gcc version ([0-9]+)\\.[0-9]+\\.[0-9]+");
    std::smatch match;
    auto gcc_major = (std::regex_search(proc_ver, match, rx) && match.size() == 2) ? match[1] : "5"s;
    int maj = 0;
    std::from_chars(gcc_major.data(), gcc_major.data() + gcc_major.size(), maj);
    std::cout << ((maj < 8) ? "1\n" : "2\n");
    return 0;
}

@throwable-one
Copy link

detect wsl1:)
uname -r | grep Microsoft > /dev/null && echo "WSL1"

@therealkenc
Copy link
Collaborator

[ $(grep -oE 'gcc version ([0-9]+)' /proc/version | awk '{print $3}') -gt 5 ] && \
    echo "WSL2" || echo "WSL1"

@andyneff
Copy link

Update (Windows 10 version 2004):

I'm brand new to WSL2, however...

  • uname -r says microsoft-standard in it, so all checks for the word [mM]icrosoft would fail
  • (opinion) The gcc method seems arbitrary, assuming WSL1 never changes to a new gcc
  • (opinion) WSL_INTEROP still works, and seems like the best test for now, but that's just my opinion

@levesquejf
Copy link

WSL2 has the directory /run/WSL/ and not WSL1.

@TuxVinyards
Copy link

I think that perhaps a belt & braces approach is needed. We have don't have any control over WSL1 getting a similar folder added. Likewise, whether 'Microsoft' has upper or lower case.

Using [[ -d "/run/WSL" ]] && echo "WSL2 present" is nice and easy, I acknowledge. But IMO a better method is to test for the underlying hyper-v running system that WSL2 uses.

Try lshw | grep smp for instance. WSL1 shows 'smp' while WSL2 will show both smp & vsyscall32.

We can write something like WSL2_Flag="$(lshw | grep vsyscall32) ; if [[ $WSL2_Flag ]] ; then echo "WSL2 present" ; fi

Or alternatively, we can look in '/proc'. We can't guarantee 'lshw' to be present in every Linux distro, although it usually is:

In WSL1 cat /proc/interrupts is empty whereas with WSL2 it is positively active, especially the line for HVS Hyper -V !

eg. WSL2_Flag="$(cat /proc/interrupts ) ; if [[ $WSL2_Flag ]] ; then echo "WSL2 present" ; fi

@vlovich
Copy link

vlovich commented Sep 9, 2020

What about the environment variable WSL_DISTRO_NAME? [[ -n "$WSL_DISTRO_NAME" ]]. Is that a suitable & cheap way to detect WSL 1 or 2?

@Biswa96
Copy link

Biswa96 commented Sep 10, 2020

No. WSL_DISTRO_NAME variable is set by /init binary blob for both WSL1 and WSL2.

@vlovich
Copy link

vlovich commented Sep 10, 2020

@Biswa96 I asked if it's suitable for WSL1 OR WSL 2, not WSL 2 vs WSL 1 so it sounds like the answer to my question is actually yes?

@therealkenc
Copy link
Collaborator

Yes but not retroactively.

@mirabilos
Copy link

mirabilos commented Sep 28, 2020

@therealkenc is the way you described (distinguish between WSL 1 and WSL 2 using the GCC version) going to be guaranteed stable, even in future versions of WSL 1?

Which is the guaranteed way to distinguish between these two and nōn-WSL Linux then? The presence of the string icrosoft?

Someone suggested checking the root filesystem type, which for WSL 1 is wslfs, but that won’t help for WSL 2. Any other filesystem can be umounted… and some people have trouble with custom kernels and the detection mechanism you provided (especially in the “WSL vs Linux” case).

Anything that needs superuser privilegues (lshw…) is also out, for hopefully obvious reasons.

@therealkenc
Copy link
Collaborator

No guarantees in life. Although given that the unspoken textual API has now irretrievably leaked into userspace, yes, I would treat regex [Mm] difference as pretty future proof.

That said, users can (and do) compile their own kernels in WSL2 and then misname the their kernel, or compile with clang, so you can't count on the kernel string in WSL2 to be anything in particular. Fortunately (notwithstanding the env pollution) recent WSL has the WSL_DISTRO_NAME and WSL_INTEROP environment variables. WSL_INTEROP is only present in WSL2. Using them is not backwards compatible with older WSL though, and they won't EOL for years.

Possible approach:

  • Test for existence WSL_DISTRO_NAME. If it exists, test for WSL_INTEROP. Yes, WSL2; no, WSL1.
  • If WSL_DISTRO_NAME does not exist, fall back on the version string regex.

@mirabilos
Copy link

This might do. I was thinking of simplifying this a bit:

  • if / is wslfs, then it’s WSL 1, else it’s either Linux/Android or WSL 2
  • if WSL_INTEROP or even WSL_DISTRO_NAME is set or the version contains icrosoft it’s WSL 2, else Linux/Android

Doing this without /proc (which is not guaranteed to be mounted) is more tricky. After statfs("/", &buf), buf.f_type is 0x53464846 on my test setup. Is this true for all WSL 1 systems?

The WSL 2 detection is even more fragile. Addition of an API from the start would have helped :/

For version detection, uname(2) exists, so this can also be done without procfs. The WSL 2 caveats apply :/

@therealkenc
Copy link
Collaborator

therealkenc commented Sep 28, 2020

Use uname(2) if you can't depend on /proc. The gcc test was belt+suspenders overkill, and given existence possibility clang probably does more harm than good. strstr(buf.release, "Microsoft") means WSL1. Else Linux/Android/WSL2. For your purposes WSL2 is Linux anyway.

@mirabilos
Copy link

mirabilos commented Sep 28, 2020 via email

@therealkenc
Copy link
Collaborator

WSL 2 probably is “don’t use” anyway

WSL2 should be perfectly fine. WSL1 looks like it might be "don't use anyway". Either that's a hard fail in your use-case, or it isn't a hard fail. If it isn't a hard fail, there is no reason for it to be a hard fail on Linux or Android either. Just let that function always succeed, and lie about success when it doesn't. If it is a hard fail, you don't need to test for WSL1. Let it fail.

The better approach than querying the OS is to test capabilities. Here, the runtime capability test is whether setsockopt(...IPV6_TCLASS...) works, not whether you are "on WSL1" or "on Linux". Maybe one day in some mythical future IPV6_TCLASS starts working on win32 (and by extension WSL1). I bring it up not for your specific use-case, but because near all of the "if(WSL)" scattered on the Interwebs are testing the wrong thing.

Perhaps an identifier can be added in a next version?

That's existence environment WSL_DISTRO_NAME for WSL plus && WSL_INTEROP to differentiate WSL1 v. WSL2. But you can't use it retroactively, so the uname(2) or /proc fallback is necessary regardless.

@mirabilos
Copy link

mirabilos commented Sep 28, 2020 via email

WyriHaximus added a commit to WyriHaximus/php-test-utilities that referenced this issue Mar 3, 2021
Based on microsoft/WSL#4555 via https://github
.com//issues/374 which has a ton more
possibilities to detect WSL.
WyriHaximus added a commit to WyriHaximus/php-test-utilities that referenced this issue Mar 3, 2021
Based on microsoft/WSL#4555 via https://github
.com//issues/374 which has a ton more
possibilities to detect WSL.
WyriHaximus added a commit to WyriHaximus/php-test-utilities that referenced this issue Mar 3, 2021
Based on microsoft/WSL#4555 via
#374 which has
a ton more possibilities to detect WSL.
WyriHaximus added a commit to WyriHaximus/php-test-utilities that referenced this issue Mar 3, 2021
Based on microsoft/WSL#4555 via
#374 which has
a ton more possibilities to detect WSL.
@t-ru
Copy link

t-ru commented Apr 4, 2021

Detecting WSL (bash code). Tested with

  • WSL 1
  • WSL 2 (standard kernel AND custom kernel)

The detection should be future safe. Code is from a new (currently not released) native systemd initialization for WSL

https://github.com/t-ru/wsl-misc/blob/master/detect-wsl-version/detect-wsl-version-v1.sh

@mirabilos
Copy link

mirabilos commented Apr 4, 2021 via email

@t-ru
Copy link

t-ru commented Apr 4, 2021

From a shell developer PoV this is horridly inefficient.

Maybe. But it's working.

Is your C-Code working under all conditions?
No.

Is your C-Code posted above inefficient?
Yepp.
Checking uname is stupid.
Checking WSL_INTEROP before WSL_DISTRO_NAME is also stupid.

You call mount twice, you can use grep -q instead of -c

many roads lead to rome

a temporary file is also not necessary,

The temporary file is required. The reason is that commands (grep, awk, head, ...) are not work on wsl.exe.
The way with temp file is always working.

I needed a C solution in the project that caused me to ask anyway.

Feel free and write your one holy grail solution.

@mirabilos
Copy link

mirabilos commented Apr 4, 2021 via email

@t-ru
Copy link

t-ru commented Apr 4, 2021

Why even use grep? Shows how little you understand the Unix toolkit and shell. (But then, you mentioned systemd, so this is to be expected.)

I know the tools very well. But I write the code on a system that has only a minimal set of packages installed. Writing tiny code on systems that have all the tools installed is easy. Another aspect is that WSL is from Microsoft. There, the clocks tick differently than usual. By the way, the code was written in less than 30 minutes.

Systemd is the devil and because it is so bad, all major Linux distributions have switched to Systemd. The problem with Systemd is not Systemd, but the users who don't understand it. It is the same as with Wayland.

I’m almost tempted to make a more efficient shell implementation of the same algorithm, but your attitude is… off-putting, at least.

The most important thing in an implementation is that it works. Optimization is phase 2. I published the code because there was no solution for the problem "detection of WSL" that works reliably so far. Feel free to adapt the code and write your own implementation.

@t-ru
Copy link

t-ru commented Apr 4, 2021

@mirabilos
Copy link

mirabilos commented Apr 4, 2021 via email

@t-ru
Copy link

t-ru commented Apr 4, 2021

Bring butter to the fishes. Post better code. I will gladly take it over.

I found code from you

/* check uname first (this is improbable to fail) */
	if ((uerr = uname(&u)) == 0) {
		if (strstr(u.release, "Microsoft") != NULL) {
			/* pretty certainly WSL 1 */
			errno = e;
			return (1);
		}
		if (strstr(u.release, "microsoft") != NULL) {
			/* probably WSL 2 */
			errno = e;
			return (2);
		}
	}


if (getenv("WSL_INTEROP") != NULL) {
		/* relatively certainly WSL 2 */
		errno = e;
		return (2);
	}
	if (getenv("WSL_DISTRO_NAME") != NULL) {
		/* relatively certainly WSL under a WSL-1/2-capable NT */
		/* since we detect WSL 1 above, this is probably 2 */
		/* assume WSL 1 when uname(2) failed, though */
		errno = e;
		return (uerr ? 1 : 2);
	}

Top code... What is with WSL and custom kernels? Many, many "if" statements. Is this good code?
No. That's "divine code"

Write better code and post it. I'm waiting!!!

joshuacwnewton added a commit to joshuacwnewton/nibabel that referenced this issue Mar 23, 2024
This commit filters the following warning:

> UserWarning: Signature b'\x00\xd0\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf\x00\x00\x00\x00\x00\x00' for
> <class 'numpy.longdouble'> does not match any known type: falling back to type probe function.
> This warnings [sic] indicates broken support for the dtype!
>  machar = _get_machar(dtype)

 To ensure that this warning is only filtered on WSL1, we try to detect WSL
 by checking for a WSL-specific string from the uname, which appears to be
 endorsed by WSL devs.
 (microsoft/WSL#4555 (comment))

 I also tried checking the `WSL_INTEROP` and `WSL_DISTRO_NAME` environment
 variables as suggested in the above linked issues, but I liked that `platform`
 was already imported inside `casting.py`.

 There is perhaps a more thorough approach where we collect all raised warnings,
 test the collected warnings, etc. but I didn't want to overcomplicate things.
joshuacwnewton added a commit to joshuacwnewton/nibabel that referenced this issue Mar 23, 2024
This commit filters the following warning:

> UserWarning: Signature b'\x00\xd0\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf\x00\x00\x00\x00\x00\x00' for
> <class 'numpy.longdouble'> does not match any known type: falling back to type probe function.
> This warnings [sic] indicates broken support for the dtype!
>  machar = _get_machar(dtype)

 To ensure that this warning is only filtered on WSL1, we try to detect WSL
 by checking for a WSL-specific string from the uname, which appears to be
 endorsed by WSL devs.
 (microsoft/WSL#4555 (comment))

 I also tried checking the `WSL_INTEROP` and `WSL_DISTRO_NAME` environment
 variables as suggested in the above linked issues, but I preferred reusing
 the `platform` module that was already imported inside `casting.py`.

 There is perhaps a more thorough approach where we collect all raised warnings,
 test the collected warnings, etc. but I didn't want to overcomplicate things.
joshuacwnewton added a commit to joshuacwnewton/nibabel that referenced this issue Mar 23, 2024
This commit filters the following warning:

> UserWarning: Signature b'\x00\xd0\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf\x00\x00\x00\x00\x00\x00' for
> <class 'numpy.longdouble'> does not match any known type: falling back to type probe function.
> This warnings [sic] indicates broken support for the dtype!
>  machar = _get_machar(dtype)

 To ensure that this warning is only filtered on WSL1, we try to detect WSL
 by checking for a WSL-specific string from the uname, which appears to be
 endorsed by WSL devs.
 (microsoft/WSL#4555 (comment))

 I also tried checking the `WSL_INTEROP` and `WSL_DISTRO_NAME` environment
 variables as suggested in the above linked issues, but I preferred reusing
 the `platform` module that was already imported inside `casting.py`.

 There is perhaps a more thorough approach where we collect all raised warnings,
 test the collected warnings, etc. but I didn't want to overcomplicate things.
joshuacwnewton added a commit to joshuacwnewton/nibabel that referenced this issue Mar 23, 2024
This commit filters the following warning:

> UserWarning: Signature b'\x00\xd0\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf\x00\x00\x00\x00\x00\x00' for
> <class 'numpy.longdouble'> does not match any known type: falling back to type probe function.
> This warnings [sic] indicates broken support for the dtype!
>  machar = _get_machar(dtype)

 To ensure that this warning is only filtered on WSL1, we try to detect WSL
 by checking for a WSL-specific string from the uname, which appears to be
 endorsed by WSL devs.
 (microsoft/WSL#4555 (comment))

 I also tried checking the `WSL_INTEROP` and `WSL_DISTRO_NAME` environment
 variables as suggested in the above linked issues, but I preferred reusing
 the `platform` module that was already imported inside `casting.py`.

 There is perhaps a more thorough approach where we collect all raised warnings,
 test the collected warnings, etc. but I didn't want to overcomplicate things.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests