Introduction
I’ve been using Linux for years now and I really like it. I wouldn’t want to work without it. If I had to work in a Windows-only environment I’d just go mad.
But some Linux-related things have been bothering me lately. Among other things these are:
- I’ve just got a shiny new notebook with a docking stations having two displays attached to it. I’d like to use the two external displays and the internal display simultaneously. And when I push the undock button on the docking station I want my notebook to switch to mobile mode automatically and back when I re-dock it. Currently docking, undocking and running nvidia optimus in Linux is a pain.
- This is stretches the term “lately” a bit: I work with some Windows programswhich don’t run properly under wine. I currently run these in a Windows virtual machine.
- I’ve upgraded to ubuntu 11.10 … I don’t like the way gnome – my window manager of choice – is going.
So, I’ve decided to give Windows as host a try, i.e. try having a Windows running on the laptop, running a Linux within a VM inside Windows and try to make them work together as seamlessly as possible.
Now I’m running a Windows 7 as host and a Ubuntu 11.10 in a VM. I can start Linux programs from within Windows with X11 forwarding. [Win]+[C] starts my gnome-terminal, [Win]+[X] starts the application starter gmrun, etc.. I have Linux programs in my Windows start menu. And each running program has its place in the task bar.
Oh, and everything is neatly responsive and fast.
Of course not everything is working seamlessly (yet?), but maybe I can get it running some day. For example Linux system tray icons don’t appear in the windows system tray.
I believe I’m not the first one having a setup like this, but since I had to gather my information from several different sources, I’d like to describe my whole setup.
Feel free to post any comments, questions or improvement ideas (!), but please don’t tell me how bad a person I am for using Windows. I know that already.
Let’s begin …
Base Setup
As I mentioned before I have a Lenovo T420 with Windows 7 running on it. I enabled the Intel virtualization features in the BIOS, installed the Lenovo update tool, joined our company’s windows domain and installed all the needed updates.
Afterwards I installed TrueCrypt and encrypted my entire disk.
Pretty basic.
Now we get to the fun part … hopefully …
The Fun Part … there we go
I assume you know Linux and Windows (at least a bit), ssh, X11 and the concept of virtual machines.
I also assume that you’ve used VirtualBox before. If not, please consult the documentation, since this won’t be a VirtualBox tutorial. Neither will it be a Cygwin tutorial.
If you like to save time, I recommend beginning to download Cygwin (setup.exe), VirtualBox and you favorite Linux ISO first. I use ubuntu 11.10, despite Unity and Gnome3. I won’t be seeing the window manager them much anyway. Afterwards I’d begin the installation of Cygwin since it will take quite a while to compile all the packages you need. The rest of the steps shouldn’t take too long to complete.
1. Install Linux In A VirtualBox VM
1.1. Install VirtualBox
We’ll run the VM within VirtualBox, so download VirtualBox and the Extension Pack from https://www.virtualbox.org/wiki/Downloads and install both of them.
1.2. Prepare The VM
- Create a new machinenamed “internal linux” with Linux / Ubuntu 64bit (if you want to use ubuntu).
- Set the “Base Memory Size” to whatever you think is necessary. I’ve set it to 1GB.
- Setup a new “Virtual Hard Disk” as “Start-Up Disk”. I’ve created a 8GB, VDI, Fixed size. It’s “only” 8GB, because I will add other dynamic disks and use a shared volume later.
- Adjust the following settings
- In “System -> Acceleration” enable “VT-x/AMD-V” and “Nested Paging” if your system supports this features. You might have to enable them in your BIOS first.
- Set “Network -> Adapter 1″ as “attached to” “Host-only Adapter”
- Enable the “Adapter 2″ and attach it to “NAT”.
- Open “Shared Folders” and add your Windows home (or whatever you like) as “internal_share”. Don’t forget the “Auto-mount” option.
Mount your favorite Linux ISO in “Storage -> IDE Controller -> the little CD-ROM symbol -> Choose a virtual CD/DVD disk file …” and start your VM.
1.3. Install Linux
Install Linux on the VM, apply the updates.
Login and install “OpenSSH Server”, “gmrun” and the “VirtualBox Guest Utils”.
internal> sudo aptitude install openssh-server gmrun virtualbox-guest-utils
For your user to be able to use the shared storage, add him/her to the group vboxsf, for example via adduser:
internal> adduser <your user> vboxsf
The newly added overlay scroll bars won’t work properly using X11 forwarding, so remove them if you like:
internal> sudo apt-get remove --purge overlay-scrollbar liboverlay-scrollbar-*
Reboot.
Maybe you want to link or adjust the mount point of the shared storage to a directory within your home.
We’ll now configure the ssh server.
1.4. Configure The OpenSSH Server
Add the following line to /etc/ssh/sshd_config and restart the ssh server:
UseDNS no
That way it won’t try to reverse-resolve your IP, thus saving connection time. (Thanks to my colleague Christoph for that hint).
Generate ssh-keypair (using ssh-keygen) without passphrase (use this only for your internal VM). Alternatively you can setup the key pair with a passphrase and use an ssh-agent in Cygwin or use a different authentication method. But since I only allow ssh access from my host-only adapter and only use this key for the internal VM, I think it’s okay.
Copy the private key to the internal_share. You’ll need it on the host system.
Copy the public key to ~/.ssh/authorized_keys.
We’ll test the connection later. We don’t have a windows ssh client, yet.
2. Prepare Windows
2.1. Install Cygwin/X
Download the setup.exe from cygwin.com and start the setup.
Select “Install from Internet“, set the root directory to %HOME%\cygwin and set the download directory to %HOME%\Downloads\cygwin.
Select the “X11” section to be installed. Also search “openssh“, select it and continue to the actual installation. This will take some time.
After the installation is finished we’ll try to connect via ssh to our VM.
You might also want to move the Cygwin setup.exe to your \Path\To\cygwin\ and put a link to that file into your Cygwin start menu folder.
Let’s test ssh.
2.2. Test The SSH Connection
Start a Cygwin Bash, which should be linked into your start menu or desktop.
cygwin> mkdir ~/.ssh
Move the ssh private key you’ve created earlier to ~/.ssh/ and adjust the rights, so only your user can use the file:
cygwin> chmod go-rwx id_rsa
Get the IP of the host-only adapter of your VM:
internal> ifconfig eth0 | grep -o -e "inet addr:\S*"
Mine is 192.168.56.101
Try to connect to your system:
cygwin> ssh internal@192.168.56.101
You should be connected to your internal VM now.
We’ll now run the XWin Server for the first time and try to run an X11 application.
2.3. Start An XWin Server
Start “Cygwin/X -> XWin Server” from your start menu. Once it’s started go back to your bash and type:
cygwin> DISPLAY=127.0.0.1:0.0 ssh -X internal@192.168.56.101
Again you should be connected to your VM. Type
cygwin> xclock
and get the xclock in your windows.
We’re finished with the basic setup. Now we’ll add some convenience add-ons to it.
3. Convenience Add-ons
3.1. run_internal.bat
Create a batch file called “run_internal.bat” somewhere, like %HOME%\bin\run_internal.bat for instance.
Add the following line and adjust the path, the user and the address according to your settings:
set DISPLAY=127.0.0.1:0.0 start /b C:\Users\MyUser\cygwin\bin\run.exe ssh -X internal@192.168.56.101 %*
We’ll use this batch file to start Linux programs with arguments – represented by the %* – tunneled through ssh using X11 forwarding.
3.2. Set Up AutoHotkey
Download, install and run AutoHotkey_L from autohotkey.com.
You should see a green H in your system tray. Right-click and click “Edit This Script“. To the script add the following lines, after adjusting your paths of course.
#x::Run, "C:\Users\MyUser\bin\run_internal.bat" "gmrun" #c::Run, "C:\Users\MyUser\bin\run_internal.bat" "gnome-terminal"
These lines will let your system run gmrun when pressing [Win][X] and gnome-terminal when pressing [Win][C].
Right-click AutoHotkey again and click “Reload This Script“.
Try it by pressing [Win]+[X] and entering “xclock [Enter]“, which should bring up xclock again.
3.3. Add The Shutdown Script
We don’t want to click our way to saving the VM before, so we’ll add a shutdown hotkey to AutoHotkey:
#s::
{
MsgBox, 1, Shutdown, Press [OK] to shutdown
IfMsgBox, OK
{
SplashTextOn, 300, 50, Saving VM, Waiting for VM to be saved
Run, "C:\Program Files\Oracle\VirtualBox\VBoxManage" "controlvm" "internal linux" savestate
SetTitleMatchMode, 1
DetectHiddenWindows, On
WinWaitClose, internal linux, , 300 ; Wait for VM to close
SplashTextOff
Shutdown, 8 ; Shut down and power down
}
}
return
It would be even nicer to hook into the shutdown process of windows. But I don’t know how, yet. Anybody?
4. Windows – Start-Up
To have all of this setup after your windows has started:
- Go to the VirtualBox Manager, right-click the VM and click “Create Shortcut on Desktop”
- Open the start menu -> all programs and right-click startup to “explore”.
- Move the links to the VM, AutoHotkey and XWin Server to the start-up folder.
5. One More Add-on – ssh-agent
Since I will use my Linux as environment to connect to the world via ssh, I only setup an ssh-agent there. I wouldn’t know how to do this on windows anyway.
This is a bit tricky, since I won’t enter a gnome-session, but just connect via ssh to the box.
5.1. PermitUserEnvironment
We’ll use ssh’s feature to be able to set environment variables for ssh sessions.
Add the following line to your /etc/ssh/sshd_config
PermitUserEnvironment yes
and restart sshd:
internal> service ssh restart
This will allow us to put environment variables into ~/.ssh/environment, thus populating SSH_AUTH_SOCK and SSH_AGENT_PID once we start ssh-agent.
5.2. Some Preparation: ~/bin
Create a home bin folder, if you like:
internal> mkdir $HOME/bin
Add the following line to your ~/.bashrc (adjust paths):
export PATH=$HOME/bin:$PATH
5.3. Create ~/bin/auto-start-ssh-agent
The following script will
- check if there $SSH_AUTH_SOCK is set and a socket and otherwise try to recover it from ~/.ssh/environment
- afterwards check if there the ssh-agent is running
- if it can’t find an ssh-agent it will start one and export the needed environment variables to ~/.ssh/environment and the context the script is run in
This script should start a new ssh-agent and export the needed information, but only when needed.
Note to self: I should check if an actual ssh-agent is bound to that socket. It’s very probable though. :)
Here’s the code (~/bin/auto-start-ssh-agent):
#!/bin/bash
SSHAGENT=/usr/bin/ssh-agent
ENVFILE=$HOME/.ssh/environment
# assumptions
agent_running=0
do_export_envs=0
# check if ssh environment variables are set
if [ ! -S "$SSH_AUTH_SOCK" ]; then
SSH_AUTH_SOCK=`grep -o -e '^SSH_AUTH_SOCK=\(.*\)$' $ENVFILE | sed 's/SSH_AUTH_SOCK=//'`
SSH_AGENT_PID=`grep -o -e '^SSH_AGENT_PID=\(.*\)$' $ENVFILE | sed 's/SSH_AGENT_PID=//'`
do_export_envs=1
fi
# check if agent is running
if [ -S "$SSH_AUTH_SOCK" -a -n "$SSH_AGENT_PID" ]; then
proc_found=`ps -fp $SSH_AGENT_PID | grep "$SSHAGENT" -c`
if [ $proc_found -eq 1 ]; then
agent_running=1
fi
fi
# start a new agent if none is found
if [ $agent_running -eq 0 ]; then
if [ -x "$SSHAGENT" ]; then
eval `$SSHAGENT`
# replace the environment variables in ENVFILE
sed -i "s@^\(SSH_AUTH_SOCK\)=\(.*\)\$@\1=$SSH_AUTH_SOCK@" $ENVFILE
sed -i "s@^\(SSH_AGENT_PID\)=\(.*\)\$@\1=$SSH_AGENT_PID@" $ENVFILE
do_export_envs=1
else
echo 'no valid ssh-agent found'
fi
fi
# export ssh environment variables if needed
if [ $do_export_envs -eq 1 ]; then
export SSH_AUTH_SOCK=$SSH_AUTH_SOCK
export SSH_AGENT_PID=$SSH_AGENT_PID
fi
Make it executable:
internal> chmod +x auto-start-ssh-agent
5.4. ~/bin/gssh
The following script will execute the auto-start-ssh-agent script to ensure there is a running ssh-agent.
It will then use ssh-add to check if the identity file as already added to the agent and otherwise let you add it.
Finally it will continue to connect via ssh using the given identity file.
So only the first time, you run gssh you should have to put in your passphrase.
Here’s the code (adjust paths) (~/bin/gssh):
#!/bin/bash IDENTITYFILE=/path/to/private/key . auto-start-ssh-agent key_found=`ssh-add -l | grep $IDENTITYFILE -c` if [ $key_found -eq 0 ]; then ssh-add $IDENTITYFILE fi ssh -i $IDENTITYFILE "$@"
Make it executable:
internal> chmod +x gssh
5.5. ssh alias (optional)
Add this to ~/.bash_aliases (or to ~/.bashrc), if you like:
alias ssh='gssh'
6. Odds And Ends
- After updating VirtualBox the host-only adapter was missing. I had to add it in the global network configuration again and set the IP to 192.168.56.1 (host system)
- After installing the nvidia NVS drivers instead of the Lenovo drivers, I had to reinstall X11 for Cygwin
7. Open Issues
- The run_internal.bat-way is not very elegant, since a box flashes every time
- The VM should be minimized to system tray
Ever thought about giving OS X a try? I did the switch several months ago and I don’t regret it at all. Most of the software that runs on Windows also runs on Mac (except for Minesweeper and Paint, maybe) and you don’t have to worry about stuff like display settings anymore. Very interesting article, though! Best regards from Berlin :)
I tried OS X and was positively amused but not entirely persuaded. I wrote an article on my first impression.
Yes, I have. I’ve been using Mac OS X on a private Laptop for about 1.5 years now, actually.
It’s fine for personal use, but …
… I can’t connect more than one external display at the screen resolution I’m using.
… I still need to run some windows programs, like Enterprise Architect.
… I still want to have a linux. I have to admit macports cover a lot of linux utils, though.