What is the entry point for WSL 1? - windows-subsystem-for-linux

Imagine we have a statically linked Linux executable.
How should I name it in the imported tar.gz so the WSL 1 will run it by default, when created and started like:
# import an archive as a WSL distro
wsl --import static tmp-root-dir static.tar.gz
# boot distro to a default app??
wsl -d static
PS WSL uses own proprietary boot process and seems doesn't use traditional Unix /sbin/init.

Short answer:
The smallest bootable (without errors or warnings) WSL rootfs will consist of three files:
/main: Your statically-linked application. It can be named whatever you want, as long as the name matches what is in passwd.
/etc/passwd: Defines the application (i.e. shell) to load for the default user.
/etc/wsl.conf: To suppress normal WSL functionality and (optionally) define the non-root user.
More detail:
This probably isn't exactly what you are wanting, but it will hopefully meet your needs.
To start with, the entry point for WSL (the first time a Linux ELF binary is started inside the instance) seems to be its /init binary, which, in addition to some "normal" Linux init process tasks, sets up some of the Windows-interop functionality. To my knowledge, it cannot currently be changed. As far as I can tell, for WSL1, it is injected into the instance by the LXSS manager when starting a WSL instance.
Note: WSL2 might be slightly different in this regard, as it does seem to use a kernel-processed initrd to load /init. It is possible to override the kernel command-line, but that would impact all WSL2 instances, so it's probably not a practical solution.
It's not quite clear from your question whether you want the "default application" to:
Run as the default application/shell every time wsl -d static is run, even if it was already running.
Or just run once when starting the WSL1 instance for the first time.
I believe you are looking for the first option.
Run as the default application
In the first case, the standard WSL1 /init process might get you to where you need to be. As part of the startup, as you would expect, it reads /etc/passwd to determine the user shell to start. It also reads /etc/wsl.conf to determine the default user ID (but falls back to the registry if there is no default user set in wsl.conf).
So, to start a different application (let's call it main), you can:
Place the binary in the root directory of your image.
Set the application as the "shell" of the root user in a single-line /etc/passwd:
user:x:1000:1000:user:/:/main
Side-note that this also sets the home directory to / so we don't have to create another directory.
Define a etc/wsl.conf with the following contents:
[user]
default=user
[automount]
enabled=false
mountFsTab=false
[interop]
appendWindowsPath=false
This will prevent WSL from performing the following startup tasks, which would produce an error without additional image support:
Mounting Windows drives into the instance
Attempting to process /etc/fstab (since we have no mount command in the image).
Appending Windows paths (since our instance won't have access to the Windows drives)
It also sets the default user to the UID 1000 user we created in /etc/passwd. This isn't strictly necessary - There's likely no concern with running as root in this single-use instance, but I've included a non-root user as a "best practice".
That should be it. The smallest bootable WSL rootfs will consist of just those three files:
/etc/wsl.conf
/etc/passwd
/main
This will work on WSL1 as well as WSL2, although for WSL2, you should invoke with wsl ~ -d static to make sure that it doesn't try to start on a Windows drive that it can't access. Otherwise, you'll receive an init error, but your application will still be invoked.
Run once
If you are looking for something that will, for instance, start up a daemon when the instance is started for the first time, then there are a few alternatives that I document in this answer. If you are on Windows 11, then there's a built-in mechanism via /etc/wsl.conf. Otherwise, on Windows 10, you'll probably need to include some binary that can handle conditional logic. Something like execline would probably be perfect for this, but I've had issues with it under WSL2, at least, and I'm not sure that it would run under WSL1 (but it might).
Side-note for WSL1/musl
musl is a commonly used alternative libc implementation. For instance, Rust (AFAICT), can only generate truly statically-linked executables using musl. Note, however, that WSL1 cannot run musl-based statically linked binaries.
WSL2 can handle them just fine.

I managed to get it working. Initially I missed an executable bit on the app when created TAR archive.
Take standard 64-bit assembly:
.data
msg:
.ascii "Hello, world!\n"
.set len, . - msg
.text
.globl _start
_start:
# write
mov $1, %rax
mov $1, %rdi
mov $msg, %rsi
mov $len, %rdx
syscall
# exit
mov $60, %rax
xor %rdi, %rdi
syscall
and create a minimal WSL system:
wsl as -64 -o minimal.o minimal.s
wsl ld -melf_x86_64 -o minimal minimal.o
tar czf minimal.tar.gz \
--mode=a=rx \
--xform='s#^minimal#/\0#' minimal
wsl --import minimal rootfs-minimal minimal.tar.gz --version 1
wsl --list
wsl -d minimal -e /minimal
To make executable default (shorten wsl -d minimal -e /minimal to wsl -d minimal) we need an extra file /etc/passwd:
root:x:0:0:root:/root:/minimal
First line of this file determine a default user and so path to the executable (entry point) unless you override the user with /etc/wsl.conf:
[user]
default=user
Basically WSL 1 treats only 2 files as magical (in addition to ignoring /sbin/init):
/etc/wsl.conf
/etc/passwd

Related

How to create a Linux GUI app short cut for WSL2 on Windows10?

I have properly installed and setup WSL2. It works fine.
I also setup X11 forwarding and X server (VcXsrv). I can launch GUI apps such like konsole or gvim or even google-chrome from a bash shell.
Now I want to launch konsole by simply double clicking a short cut on the desktop without launching the bash command mode terminal. How should I do it?
I tried running this in cmd:
> wsl /usr/bin/konsole
and it reports:
qt.qpa.xcb: could not connect to display
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, wayland-egl, wayland, wayland-xcomposite-egl, wayland-xcomposite-glx, xcb.
I'm guessing it is because some X11 forwarding configurations were not properly setup, so I created a k.sh as follows:
#!/usr/bin/bash
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
export LIBGL_ALWAYS_INDIRECT=1
/usr/bin/konsole &
The first two lines were the X11 settings in my .bashrc, the last line launches konsole.
It works fine under bash environment; but when I ran
wsl k.sh
from windows cmd environment, it silently quitted without launching the konsole.
I'm out of ideas. What should I do to directly launch konsole or other Linux GUI apps under windows without having to getting into bash?
Thanks in advance.
You are asking about two different command-lines, and while the failures in running them via the wsl command have the same root-cause, the underlying failures are likely slightly different.
In both cases, the wsl <command> invocation results in a non-login, non-interactive shell where the command simply "runs and exits".
Since the shell is non-login/non-interactive, your startup files (such as ~/.bashrc and ~/.bash_profile, among others) are not being processed.
When you run:
wsl /usr/bin/konsole
... the DISPLAY variable is not set, since, as you said, you normally set it in your ~/.bashrc.
Try using:
wsl -e bash -lic "/usr/bin/konsole"
That will force bash to run as a login (-l), interactive (-i) shell. The DISPLAY should be set correctly, and it should run konsole.
Note that the quotes probably aren't necessary in this case, but are useful for delineating the commands you are passing to bash. More complicated command-lines can be passed in via the quotes.
As for:
wsl k.sh
That's likely a similar problem. You are doing the right thing by setting DISPLAY in your script, but I notice that you aren't using a fully-qualified path it. This would normally work, of course, if your script is in a directory on the $PATH.
But I'm guessing that you might add that directory to the $PATH in your startup config, which means (again) that it isn't being set in this non-login, non-interactive shell.
As before, try:
wsl -e bash -lic "k.sh"`
You could also use a fully-qualified path, of course.
And, I'm fairly sure you are going to run into an issue with trying to put konsole in the background via the script. When WSL exits, and the bash shell process ends, the child konsole process will terminate as well.
You could get around this with a nohup in the script, but then you also need to redirect the stderr. It's probably easiest just to move the & from the script itself to the command-line. Change your k.sh to:
#!/usr/bin/bash
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
export LIBGL_ALWAYS_INDIRECT=1
/usr/bin/konsole
Then run it with:
wsl -e bash -lic "k.sh &"`
Finally, a side note that when and if you can upgrade to Windows 11, it will automatically create Windows Start Menu entries for any Linux GUI app you install that creates a .desktop file. You can manually create .desktop files to have WSL create Start menu items for most applications.
For reference, in Windows 11 it's easier. To run a GUI application without a terminal window popping up, you just need to call wslg.exe instead of wsl.exe.
So, for example:
target: C:\Windows\System32\wslg.exe konsole
start in: C:\WINDOWS\system32
shortcut key: None
comment: Konsole
This tutorial shows how to install VcXsrv and and edit .bashrc to ensure that the "DISPLAY env var is updated on every restart".
DISPLAY env var needs to be dynamic setting.
I've used it successfully with WSL2 on Windows10 Version 21H2 (OS build 19044.2130) to run Chrome, Edge, and thunar. I'm using the Ubuntu 20.04 Linux distro.
To edit .bashrc follow these instructions.

What is the difference between calling a command via "wsl [command]" and opening a wsl shell and calling "[command]"?

I am using Ubuntu via WSL 2.0 on Windows 10 and would like to run Texlive from the Windows command line. To do so I prepended the Texlive folder to the path in /etc/environment (I also tried a number of other locations eg. $HOME/.bashrc):
C:\Users\scott\Documents>wsl echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:...
C:\Users\scott\Documents>wsl
scott#SCOTT-PC:/mnt/c/Users/scott/Documents$ echo $PATH
/usr/local/texlive/2020/bin/x86_64-linux:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:...
Why is there a difference between these two paths? Is it possible to change the first PATH variable?
To be honest, when I first looked at this question, I thought it would be an easy answer. Oh how wrong I was. There are a lot of nuances to how this works.
Let's start with the fairly "easy" part, though. The main difference between the first method and the second:
wsl by itself launches into a login (and interactive) shell
the shell launched with wsl echo $PATH is neither a login shell nor an interactive shell
So the first will source both login scripts (e.g. ~/.profile) and interactive startup scripts (e.g. ~/.bashrc). The second form does not get to source either of these.
You can see this a different way (and get to the solution) with the following commands:
wsl -e bash -c 'echo $PATH'
wsl -e bash -li -c 'echo $PATH'
The -li forces bash to run as a login and interactive shell, thus sourcing all of the applicable startup scripts. And, as #bovquier points out in the comments, a single quote is needed here to prevent PowerShell from interpolating the $ before it gets to Bash. That, or escape it.
You should be able to run TeX Live the same way, just replacing the "echo $PATH" with the startup command you need for TeX Live.
A second option would be to create a script that both adds the path and runs the command, and just launch that script through wsl /path/to/script.sh
That said, I honestly don't think that your current login/interactive PATH is coming from /etc/environment. In my testing, at least, /etc/environment has no use in WSL, and that's to be expected. /etc/environment is only sourced by PAM modules, and with no login check performed by WSL, there's no reason to invoke PAM in either the wsl nor the wsl echo $PATH commands.
I'd expect that you still have the PATH setting in ~/.bashrc or somewhere similar), and that's where the shell is picking it up from at the moment.
While this isn't necessarily critical to understanding the answer, you might also wonder, if /etc/environment isn't used for setting the default (non-login, non-interactive) path in WSL, what is? The answer seems to be that it is hard-coded into the init that starts up WSL. That init is also what appends the Windows path (assuming you don't have that feature disabled in /etc/wsl.conf).

"Windows Subsystem for Linux has no installed distributions" even though 'Ubuntu' is installed

I recently moved my wsl directory to another drive due to low storage in C: drive. As per the answer provided in this StackOverflow post, I used lxrunoffline tool and moved my Ubuntu distribution to another drive (E:\wsl in my case). As soon as the distribution was moved successfully, I ran wsl to test and it worked like a charm.
Everything went fine until one day I accidentally renamed the E:\wsl folder to something else. Well, as expected, wsl didn't work. Then, I reverted back to the name wsl and expected it to work but to my surprise, it didn't find any installed distribution after that even though it's installed... πŸ˜•
E:> wsl
Windows Subsystem for Linux has no installed distributions.
Distributions can be installed by visiting the Microsoft Store:
https://aka.ms/wslstore
Is there any way to revert back to the old directory or make wsl point to a manual location?
EDIT: I don't want to reset Ubuntu as I want to retain the installed packages and preferences...
Well, I finally found a solution to this problem. 😊
This is as simple as registering the distribution using lxrunoffline tool using the rg or register command.
E:\LxRunOffline\LxRunOffline-v3.3.3>lxrunoffline rg
[ERROR] the option '-d' is required but missing
Options:
-n arg Name of the distribution
-d arg The directory containing the distribution.
-c arg The config file to use. This argument is optional.
After running the register command, I was able to start wsl as usual. But that would log you in as a "root" user and would thus start in "/root" directory. I ran the following command to start wsl as different user (this is for Ubuntu):
ubuntu config --default-user <user-name>

Access pagemap in gem5 FS mode

I am trying to run an application which uses pagemap in gem5 FS mode.
But I am not able to use pagemap in gem5. It throws below error -
"assert(pagemap>=0) failed"
The line of code is:
int pagemap = open("/proc/self/pagemap", O_RDONLY);
assert(pagemap >= 0);
Also, If I try to run my application on gem5 terminal with sudo ,it throws error-
sudo command not found
How can I use sudo in gem5 ??
These problems are not gem5 specific, but rather image / Linux specific, and would likely happen on any simulator or real hardware. So I recommend that you remove gem5 from the equation completely, and ask a Linux or image specific question next time, saying exactly what image your are using, kernel configs, and provide a minimal C example that reproduces the problem: this will greatly improve the probability that you will get help.
I have just done open("/proc/self/pagemap", O_RDONLY) successfully with: this program and on this fs.py setup on aarch64, see also these comments.
If /proc/<pid>/pagemap is not present for any file, do the following:
ensure that procfs is mounted on /proc. This is normally done with an fstab entry of type:
proc /proc proc defaults 0 0
but your init script needs to use fstab as well.
Alternatively, you can mount proc manually with:
mount -t proc proc proc/
you will likely want to ensure that /sys and /dev are mounted as well.
grep the kernel to see if there is some config controlling the file creation.
These kinds of things are often easy to find without knowing anything about the kernel.
If I do:
git grep '"pagemap'
to find the pagemap string, which is likely the creation point, on v4.18 this leads me to fs/proc/base.c, which contains:
#ifdef CONFIG_PROC_PAGE_MONITOR
REG("pagemap", S_IRUSR, proc_pagemap_operations),
#endif
so make sure CONFIG_PROC_PAGE_MONITOR is set.
sudo: most embedded / simulator images don't have it, you just login as root directly and can do anything by default without it. This can be seen by the conventional # in the prompt instead of $.

Is there a way I can have a VM gain access to my computer?

I would like to have a VM to look at how applications appear and to develop OS-specific applications, however, I want to keep all my code on my Windows machine so if I decide to nuke a VM or anything like that, it's all still there.
If it matters, I'm using VirtualBox.
This is usually handled with network shares. Share your code folder from your host machine and access it from the VMs.
Aside from network shares, another tool to use for this is a version-control system.
You should always be able make a normal network connection between the VM and the hosting OS, as though it were another computer on the same network. Which, in some sense, it is.
I do this all the time.
I have a directory in a Windows drive that I mount in my host ubuntu 12.04.
I run virtualbox ubuntu 13.04 as a guest.
I want the guest to mount the Windows directory with full non-root permissions.
I do almost all my work from a bash shell, so this method is natural for me.
When searching for methods to automatically mount virtualbox shared folders,
reliable and correct methods are hard to distinguish from those that fail.
Failures include getting and setting permissions, as well as other problems.
Methods that fail include:
modifying /etc/fstab
modifying /etc/rc.local
I am fairly certain that rc.local can be used,
but no methods I have tried worked.
I welcome improvements on these guidelines.
On virtualbox 4.2.14 running nautilus (bash terminal) on an ubuntu 13.04 guest,
Below is a working method to mount Common (sharename)
on /home/$USER/Desktop/Common (mountpoint) with full permissions.
(Note the β€˜\’ command continuation character in the find command.)
First time only: create your mountpoint, modify your .bashrc file, and run it.
Respond with password when requested.
These are the four command-lines needed:
mkdir $HOME/Desktop/Common
sudo echo β€œ$USER ALL=(ALL) NOPASSWD:ALL” >> /etc/sudoers
find $HOME/Desktop/Common -maxdepth 0 -type d -empty -exec sudo \
mount -t vboxsf -o \
uid=`id -u $USER`,gid=`id -g $USER` Common $HOME/Desktop/Common \;
source ~/.bashrc # Needed if you want to mount Common in this bash.
All other times: simply launch a bash shell.
The find command mounts the shared directory if the mountpoint directory is empty.
If the mountpoint directory is not empty, it does not run the mount command.
I hope this is error-free and sufficiently general.
Please let me know of corrections and improvements.