Time hackery in VirtualBox

:: hacking, virtualbox, virtualization, vm, windows

By: Maciej Barć

Unwanted (VDI) present

Recently I was given a task to run a outdated piece of commercial software on a very outdated (in)famous operating system. Unlucky for me on the given VDI (VirtualBox disk image) the so-called “Guests Additions” that enabled “better” time synchronization were already installed.

Fighting VBox time

So, this is what I had to do to keep the old clock:

  • turn off networking card in the virtual machine configuration, because we do not want the system to use NTP or other newt service to get the “new” time,
  • disable getting the host’s clock time: VBoxManage setextradata "WinXP" "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled" 1,
  • and… this magic: VBoxManage modifyvm "WinXP" --biossystemtimeoffset "-341597644449", this thing does the very weird thing of telling the virtual machine BIOS how much to set it back in time, the offset here is total milliseconds between some old date and current date.

Getting the old date

With the Python script snippet below you will get the milliseconds to set back the VirtualBox clock to 01.01.2012 01:00.

1
2
3
4
from datetime import datetime

round((datetime.strptime("01.01.2012 01:00", "%d.%m.%Y %H:%M")
      - datetime.now()).total_seconds() * 1000)

Comparing objects in Racket

:: lisp, programming language, racket, scheme, tutorial

By: Maciej Barć

Equality methods

By implementing a method for equality equal-to? and two extraction methods equal-hash-code-of and equal-secondary-hash-code-of we can define our own object comparison rules.

For more info see Object Equality and Hashing.

Consider the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(define integer%
  (class* object% (equal<%>)
    (super-new)

    (init-field [value 0])

    (define/public (equal-to? other-object recur)
      (= value (get-field value other-object)))

    (define/public (equal-hash-code-of hash-code)
      (hash-code value))

    (define/public (equal-secondary-hash-code-of hash-code)
      (hash-code value))))

If we create a new integer% object we can notice that it is not transparent (we can not inspect values of any of it’s fields).

1
2
(new integer%)
;;  => (object:integer% ...)

But if we compare two fresh integer% objects they will be equal.

1
2
(equal? (new integer%) (new integer%))
;;  => #true

Transparent class

A transparent cvlass is a class with the inspect expression valuye se to #false.

From Racket documentation Creating Classes:

Just as for structure types, an inspector controls access to the class’s fields, including private fields, and also affects comparisons using equal?.

Consider the following example:

1
2
3
4
5
6
7
8
(define integer%
  (class object%

    (super-new)

    (inspect #false)

    (init-field [value 0])))

If we create a new integer% object we can see it’s field values.

1
2
(new integer%)
;;  => (object:integer% 0)

And if we compare two fresh integer% objects they will be equal.

1
2
(equal? (new integer%) (new integer%))
;;  => #true

Runing nginx under a local user

:: admin, http, network, nginx, server, tutorial

By: Maciej Barć

Config

First let’s prepare a suitable nginx configuration file.

This one is pretty bare but it works well for our case:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
worker_processes 1;
daemon off;
pid ./nginx/temp/nginx.pid;

error_log /dev/stdout info;

events {
    worker_connections 1024;
}

http {
    client_body_temp_path ./nginx/temp/client 1 2;
    proxy_temp_path ./nginx/temp/proxy;
    fastcgi_temp_path ./nginx/temp/fastcgi;
    uwsgi_temp_path ./nginx/temp/uwsgi;
    scgi_temp_path ./nginx/temp/scgi;

    server {
        listen 127.0.0.1:8080;
        server_name localhost;

        access_log /dev/stdout;
        error_log /dev/stdout info;

        root ./;

        location / {
            autoindex on;
        }
    }
}

Server config is set up for serving all static files from the current directory.

Startup

Preparation

Based on how you want to store _temp_path files it might be necessary to create (or clean up) additional directories, for example:

1
2
rm -r ./nginx/temp
mkdir -p ./nginx/temp

Run in current directory

1
nginx -c ./nginx.conf -p ./

BTW, you may want to replace ./ with "$(pwd)" and occurrences in the config with static paths.

Bonus: other simple servers

Some of no-dependency-except-itself http servers it’s good to know about:

Python http.server

1
python3 -m http.server -b 127.0.0.1 8080

Busybox

1
busybox httpd -f -p 127.0.0.1:8080 -v

You can read more about configuring busybox’s httpd on OpenWRT docs.

Bubblewrap cross-architecture chroot

:: chroot, emulation, gentoo, linux, sandbox, system, tutorial, virtualization, vm

By: Maciej Barć

System preparation

Qemu

Emerge qemu with static-user USE enabled and your wanted architectures.

1
2
3
4
5
6
7
8
app-emulation/qemu      QEMU_SOFTMMU_TARGETS: aarch64 arm x86_64
app-emulation/qemu      QEMU_USER_TARGETS: aarch64 arm x86_64

app-emulation/qemu      static-user
dev-libs/glib           static-libs
sys-apps/attr           static-libs
sys-libs/zlib           static-libs
dev-libs/libpcre2       static-libs

OpenRC

Enable qemu-binfmt:

1
rc-update add qemu-binfmt default

Start qemu-binfmt:

1
rc-service qemu-binfmt start

Chrooting

  • select chroot location (eg /chroots/gentoo-arm64-musl-stable)
  • unpack the desired rootfs
  • create needed directories
    • mkdir -p /chroots/gentoo-arm64-musl-stable/var/cache/distfiles
  • execute bwrap
    • with last ro-bind mount the qemu emulator binary (eg qemu-aarch64)
    • execute the mounted emulator binary giving it a shell program (eg bash)

Chroot with bwrap:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
bwrap                                                       \
    --bind /chroots/gentoo-arm64-musl-stable /              \
    --dev /dev                                              \
    --proc /proc                                            \
    --perms 1777 --tmpfs /dev/shm                           \
    --tmpfs /run                                            \
    --ro-bind /etc/resolv.conf /etc/resolv.conf             \
    --bind /var/cache/distfiles /var/cache/distfiles        \
    --ro-bind /usr/bin/qemu-aarch64 /usr/bin/qemu-aarch64   \
    /usr/bin/qemu-aarch64 /bin/bash -l

Libvirt with bridge network

:: libvirt, virtualization, vm, kvm, system, tutorial, linux

By: Maciej Barć

User-mode

By default you would probably have something like this, the user-mode network:

1
2
3
4
5
<interface type="user">
  <mac address="00:00:00:00:00:00"/>
  <model type="virtio"/>
  <address type="pci" domain="0x0000" bus="0x01" slot="0x00" function="0x0"/>
</interface>

Bridge

Bridges can be easily created using the NetworkManager’s TUI tool called nmtui.

Bridge XML configuration for Libvirt

1
2
3
4
5
6
7
8
<interface type="bridge">
  <mac address="00:00:00:00:00:00"/>
  <source bridge="br1"/>
  <target dev="vnet2"/>
  <model type="virtio"/>
  <alias name="net0"/>
  <address type="pci" domain="0x0000" bus="0x06" slot="0x00" function="0x0"/>
</interface>

Sysctl options

Be sure the following options are enabled (1):

  • net.ipv4.ip_forward
  • net.ipv4.conf.all.send_redirects

and the following options are disabled (0):

  • net.bridge.bridge-nf-call-iptables

Installing PowerShell modules via Portage

:: dotnet, gentoo, packaging, portage, powershell

By: Maciej Barć

Building PowerShell

As a part of my work of modernizing the way .NET SDK packages are distributed in Gentoo I delved into packaging a from-source build of PowerShell for Gentoo using the dotnet-pkg eclass.

Packaging pwsh was a little tricky but I got a lot of help from reading the Alpine Linux’s APKBUILD. I had to generate special C# code bindings with ResGen and repackage the PowerShell tarball. Other than this trick, restoring and building PowerShell was pretty straight forward with the NuGet package management support from the dotnet-pkg.eclass.

Alternatively if you do not want to build PowerShell you can install the binary package, I have in plans to keep that package around even after we get the non-binary app-shells/pwsh into the official Gentoo ebuild repository.

Why install modules via Portage?

But why stop on PowerShell when we can also package multiple PS modules?

Installing modules via Portage has many benefits:

  • better version control,
  • more control over global install,
  • no need to enable PS Gallery,
  • sandboxed builds,
  • using system .NET runtime.

Merging the modules

PowerShell’s method of finding modules is at follows: check paths from the PSModulePath environment variable for directories containing valid .psd1 files which define the PS modules.

By default pwsh tries to find modules in paths:

  • user’s modules directory — ~/.local/share/powershell/Modules
  • system modules directory in /usr/local/usr/local/share/powershell/Modules
  • Modules directory inside the pwsh home — for example /usr/share/pwsh-7.3/Modules

Because we do not want to touch either /usr/local nor pwsh home, we embed a special environment variable inside the pwsh launcher script to extend the path where pwsh looks for PS modules. The new module directory is located at /usr/share/GentooPowerShell/Modules.

1
2
dotnet-pkg-utils_append_launchervar \
    'PSModulePath="${PSModulePath}:/usr/share/GentooPowerShell/Modules:"'

So every PowerShell module will install it’s files inside /usr/share/GentooPowerShell/Modules.

To follow PS module location convention we add to that path a segment for the real module name and a segment for module version. This also enables us to have proper multi-slotting because most of the time the modules will not block installing other versions.

Take a look at this example from the app-pwsh/posh-dotnet–1.2.3 ebuild:

1
2
3
4
5
6
src_install() {
    insinto /usr/share/GentooPowerShell/Modules/${PN}/${PV}
    doins ${PN}.psd1 ${PN}.psm1

    einstalldocs
}

And that is it. Some packages do not even need to be compiled, they just need files placed into specific location. But when compilation of C# code is needed we have dotnet-pkg to help.