Skip to content

Instantly share code, notes, and snippets.

@extremecoders-re
Last active November 2, 2024 10:24
Show Gist options
  • Save extremecoders-re/e45f0a75086b19d17b6ab86ff4387000 to your computer and use it in GitHub Desktop.
Save extremecoders-re/e45f0a75086b19d17b6ab86ff4387000 to your computer and use it in GitHub Desktop.
Run Genymotion on a Headless server

As per the official documentation it's not possible to run Genymotion on a headless server. This is because Genymotion does the UI processing outside the VM on the host. If the host is headless, i.e. doesn't have hardware acceleration, Genymotion won't be able to render the Android OS display. As a result the VM will boot up in VirtualBox but there won't be any display.

(Note that getting a GUI via X11 Forwarding, XRDP, X2Go is not the same as a dedicated display. These are virtual displays. The Genymotion app will start but on booting an Android VM the player window will be blank.)

Quoting from https://stackoverflow.com/a/39448004/1833653

When you start a Genymotion device on the standard way from Genymotion Soft, the Android OS is starting inside a VirtualBox VM but all the UI processing (which uses OpenGL) is done outside the VM to make the rendering pipeline uses your computer's GPU. Using this hardware acceleration makes the Genymotion devices fairly smooth and fast.

When you start the Genymotion VMs directly from VirtualBox, the OS will start but the rendering won't be hardware accelerated. From 2.3 to 4.2 there is a fallback solution: the rendering will be computed by the CPU, from inside the VM. From 4.3, the soft rendering is not a good solution as it will slow down the OS too much to be acceptable, that's the reason why we've disabled it and it cannot be enabled.

However this is not entirely true. Genymotion by design is a client-server application. The Genymotion player which renders the UI is entirely a separate part from the Virtualbox VM. The player and the Android OS (running inside VirtualBox) communicate over the network. So if we separate these two parts, i.e. keep VirtualBox on the headless server and move the Genymotion app to a machine which has a dedicated display, this should be possible.

This is actually totally possible using IPtables magic and spoofing the VBoxManage command which Genymotion uses to communicate with VirtualBox.

IP Adresses

GENYMOTION_IP=192.168.59.102  # Host only IP of the Android VM running in VirtualBox
SERVER_IP=169.254.166.118     # IP of the machine where VirtualBox is installed
CLIENT_IP=169.254.170.151     # IP of the client where Genymotion player is running
GENYMOTION_NIC=vboxnet3	
SERVER_NIC=enxd0374542b8e9
CLIENT_NIC=ens35

On Server (May be headless)

Run remote VBoxManage script

python remoteVBoxManage.py

Set up traffic redirection. Can be done in any of the two ways:

  1. Use port formwarding with socat
    socat -d -d  tcp4-listen:5555,reuseaddr,fork,bind=$SERVER_IP tcp:$GENYMOTION_IP:5555
    socat -d -d  tcp4-listen:22468,reuseaddr,fork,bind=$SERVER_IP tcp:$GENYMOTION_IP:22468
    socat -d -d  tcp4-listen:24810,reuseaddr,fork,bind=$SERVER_IP tcp:$GENYMOTION_IP:24810
    socat -d -d  tcp4-listen:24800,reuseaddr,fork,bind=$SERVER_IP tcp:$GENYMOTION_IP:24800
    socat -d -d  tcp4-listen:25000,reuseaddr,fork,bind=$SERVER_IP tcp:$GENYMOTION_IP:25000
    socat -d -d  tcp4-listen:6379,reuseaddr,fork,bind=$SERVER_IP tcp:$GENYMOTION_IP:6379
    
  2. Use iptables to set up a redirect
    sudo iptables -t nat -A PREROUTING -i $SERVER_NIC -p tcp -d $SERVER_IP --match multiport --dports 5555,22468,24810,24800,25000,6379 -j DNAT --to-destination $GENYMOTION_IP
    sudo iptables -t nat -A POSTROUTING -o $GENYMOTION_NIC -p tcp -d $GENYMOTION_IP --match multiport --dports 5555,22468,24810,24800,25000,6379 -j MASQUERADE
    
    OR
    sudo iptables -t mangle -A PREROUTING -i $SERVER_NIC -p tcp -d $SERVER_IP --match multiport --dports 5555,22468,24810,24800,25000,6379 -j MARK --set-mark 0x1337
    sudo iptables -t nat -A PREROUTING -i $SERVER_NIC -p tcp -m mark --mark 0x1337 -j DNAT --to-destination $GENYMOTION_IP
    sudo iptables -t nat -A POSTROUTING -o $GENYMOTION_NIC -p tcp -m mark --mark 0x1337 -j MASQUERADE
    

On client (Must have dedicated display)

Redirect traffic destined for GENYMOTION_IP to SERVER_IP

sudo iptables -t nat -A OUTPUT -p tcp -d $GENYMOTION_IP -j DNAT --to-destination $SERVER_IP
sudo iptables -t nat -A POSTROUTING -o $CLIENT_NIC -p tcp -d $SERVER_IP -j SNAT --to-source $CLIENT_IP

For some reason MASQUERADE doesn't work, so have to use SNAT as above

# This doesn't work
sudo iptables -t nat -A POSTROUTING -o $CLIENT_NIC -p tcp -d $SERVER_IP -j MASQUERADE  

Run genymotion, override path of VBoxManage. Genymotion VMs can be listed by genyshell [Source]

export SERVER_IP
PATH=/home/ec/Desktop/genymotion/plugins/:$PATH ./genyshell -c "devices list"
PATH=/home/ec/Desktop/genymotion/plugins/:$PATH ./player --vm-name "Android M"

To remove an iptables rule, list them line by line and then delete the corresponding one

sudo iptables -t nat -L --line-number
del: sudo iptables -t nat -D OUTPUT 1
@extremecoders-re
Copy link
Author

extremecoders-re commented Dec 5, 2019

remoteVBoxManage.py

Run this on server. This purpose of script is to execute the real VBoxManage and send the results back.

#!/usr/bin/env python2
import subprocess

import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer

def exec_vboxmanage(args):
    print '[+]', args
    return subprocess.check_output(["/usr/bin/VBoxManage"]+args)

server = SimpleXMLRPCServer(("0.0.0.0", 8000))
print "Listening on port 8000..."
server.register_function(exec_vboxmanage, "exec_vboxmanage")
server.serve_forever()

@extremecoders-re
Copy link
Author

extremecoders-re commented Dec 5, 2019

VBoxManage on client

Place this python script in ~/.local/bin directory. The purpose of the script is to emulate the behavior of the actual VBoxManage program installed on the headless server.

#!/usr/bin/env python
import os
import sys 
import xmlrpclib

SERVER_IP = os.getenv("SERVER_IP")
proxy = xmlrpclib.ServerProxy("http://{}:8000/".format(SERVER_IP))
print proxy.exec_vboxmanage(sys.argv[1:])

Ensure that $HOME/.local/bin is in PATH, if not add the following lines to ~/.profile.

# Add .local/bin to PATH
PATH="$PATH:$HOME/.local/bin"

@extremecoders-re
Copy link
Author

extremecoders-re commented Dec 5, 2019

Alternative approach

On client (With dedicated display)

VBoxManage

#!/bin/sh
!/bin/sh
echo "VBoxManage $@ ;exit" | /bin/nc 127.0.0.1 4444

Place in ~/.local/bin. Make sure its in PATH

IPtables redirect

Redirect traffic targeted towards GENYMOTION_IP to localhost.

sudo iptables -t nat -A OUTPUT -p tcp -d $GENYMOTION_IP -j REDIRECT

Local SSH port forwarding

Forward traffic received on localhost to remote server

ssh -nNT -L 4444:127.0.0.1:4444 -L 5555:$GENYMOTION_IP:5555 -L 22468:$GENYMOTION_IP:22468 -L 24810:$GENYMOTION_IP:24810 -L 24800:$GENYMOTION_IP:24800 -L 25000:$GENYMOTION_IP:25000 -L 6379:$GENYMOTION_IP:6379 [email protected]

On server (Headless)

socat TCP4-LISTEN:4444,bind=127.0.0.1,fork,reuseaddr EXEC:/bin/sh

Manage Genymotion VM's

List VM's

$ ./genyshell -c "devices list"

Run VM

$ ./player --vm-name '"Android M"'

Start a VM using VBoxManage

$ VBoxManage startvm "Android M" --type headless

Get IP address assigned to Genymotion vm

$ VBoxManage guestproperty get "Android M" androvm_ip_management

@extremecoders-re
Copy link
Author

extremecoders-re commented Apr 8, 2020

To reduce genymotion CPU usage, specify the QT_QUICK_BACKEND environment variable.

https://doc.qt.io/qt-5/qtquick-visualcanvas-adaptations.html#switch-between-adaptations-in-your-application

QT_QUICK_BACKEND=software|d3d12|openvg
$ QT_QUICK_BACKEND=software ./genymotion

OR

$ QMLSCENE_DEVICE=softwarecontext ./genymotion

This disables hardware acceleration.

@extremecoders-re
Copy link
Author

extremecoders-re commented Apr 8, 2020

Sign-in from commandline

$ ./gmtool config store_credentials=on
$ ./gmtool config username=johndoe password=hunter2

If you have a license key, i.e. not for personal usage

$ ./gmtool license register "92815c16-d642-4d8b-b4e2-bbff5217e85f"

https://docs.genymotion.com/desktop/3.0/04_Tools/045_GMTool.html

@extremecoders-re
Copy link
Author

extremecoders-re commented Apr 8, 2020

genysetup.sh (Single script to automate everything above - Run on client)

#!/bin/sh

GENYMOTION_IP=192.168.56.102
SERVER_IP=169.254.166.118
SERVER_USER=bb

trap_ctrlc()
{
    echo "[+] Deleting iptables rule.."
    sudo iptables -t nat -D OUTPUT -p tcp -d $GENYMOTION_IP -j REDIRECT
    exit 2
}

# initialise trap to call trap_ctrlc function
# when signal 2 (SIGINT) is received
trap "trap_ctrlc" 2

echo "[+] Genymotion IP:" $GENYMOTION_IP
echo "[+] Server IP:" $SERVER_IP

echo "[+] Setting up iptables rule...(please enter sudo password)"
sudo iptables -t nat -A OUTPUT -p tcp -d $GENYMOTION_IP -j REDIRECT

echo "[+] Setting up SSH tunnel to server, press Ctrl+C to exit"
ssh -nT \
    -L 4444:127.0.0.1:4444 \
    -L 5555:$GENYMOTION_IP:5555 \
    -L 22468:$GENYMOTION_IP:22468 \
    -L 24810:$GENYMOTION_IP:24810 \
    -L 24800:$GENYMOTION_IP:24800 \
    -L 25000:$GENYMOTION_IP:25000 \
    -L 6379:$GENYMOTION_IP:6379 \
    $SERVER_USER@$SERVER_IP 'pkill socat || true && socat TCP4-LISTEN:4444,bind=127.0.0.1,fork,reuseaddr EXEC:/bin/sh'


echo "[!] Deleting iptables rule..."
sudo iptables -t nat -D OUTPUT -p tcp -d $GENYMOTION_IP -j REDIRECT

@extremecoders-re
Copy link
Author

VBoxManage modifyvm "Android M" --hostonlyadapter1 vboxnet0
VBoxManage list hostonlyifs
VBoxManage hostonlyif remove vboxnet1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment