The mind is like a parachute. If it doesn't open, you're meat.

Building a vagrant guest plugin for QNX

19 Oct 2013

The yak shaving expedition continues...

Previously I set out to create a base box for QNX and naively expected to do a vagrant up and have everything work. HA!

Vagrant was very offended at my presumption and started going on about how it was impossible to recognise the OS I was trying to use.

So I entered the magical world of vagrant plugins and specifically guest and guest capability plugins

Let me start by saying that the vagrant API is beautifuly done. Simple, flexible and cleanly coded. The only downside, which is a result of this flexibility, is that you won't ever get an exhaustive reference. You have to dive into the code!

Now, to build a guest plugin there are four things that need to be defined:


The guest plugin needs it's name and one method, detect? and looks like this

require "vagrant"

module VagrantPlugins
  module GuestQNX
    class Guest < Vagrant.plugin("2", :guest)
      def detect?(machine)
        machine.communicate.test('uname -s | grep QNX')

We did say that QNX is BSD-like, didn't we?

Guest capabilities

With a bit of trial and error I figured out the minimum amount of capabilities that a guest plugin must have to survive booting and shutting down.

These are

I added change_hostname just because it was easy.

Instead of listing here the implementation code and make this post miles long with copy-paste-able information why don't you head on to github and browse at leisure?

What took you so long?

So a few things that I had to get right for this plugin to work.

Gem structure

I'm not really sure, but a file named exactly like your plugin (in this case vagrant-guest-qnx.rb) at the top of your gem's structure seems to be what vagrant expects. Didn't actually see it documented anywhere but I browsed several 3rd party plugins and copied the structure from vagrant-berkshelf.

I lifted the handy loader guard there as well:

 require 'vagrant'
rescue LoadError
 raise 'The Vagrant Guest QNX plugin must be run within vagrant'

So this code won't give you ugly stacktraces by chance.

The shell

This actually cost quite a bit of time. Vagrant generally has awesome logging, very detailed, but in this case it kept exiting with "no error message" so I had to dig in and use the magical powers of sprinkled puts. Turns out all machine.communicate.execute calls were returning exit status 126.

Now for all you youngsters out there, 126 and 127 are usually shell code for "bloody hell, I can't find what you want me to run". And as it turns out, when communicating over ssh vagrant defaults to running everything with bash.

Unfortunately bash is not available on stock QNX which uses ksh and links it to /bin/sh. So we need = "sh"

in the Vagrantfile.

Shared folders

There are no VMWare Tools or Guest Additions for QNX. I don't expect there will ever be. Yet Vagrant tries to establish a share between host and the VM and (in by now typical fashion) ends up hanging forever. So one more addition to the Vagrantfile is needed

config.vm.synced_folder ".", "/vagrant", :disabled => true

And that's it!


This is the bare minimum Vagrantfile needed to operate the base box you created by faithfully following my instructions

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| = "qnx65"
  config.vm.box_url = "" = "sh"
  config.vm.synced_folder ".", "/vagrant", :disabled => true

You will need to install the vagrant-guest-qnx plugin (yes, it's up on rubygems)

vagrant plugin install vagrant-guest-qnx

There are also a few constraints on the QNX VM:

Because there is no sudo in the stock VM everything is done with su. For that reason the default no-password setting for root is still there. For the purposes of this yak shaving expedition that is more than enough. There is a sudo package for QNX and if by some miracle someone is interested, a pull request will be very welcome.

Halting does not shutdown very fast. Although shutdown is performed, because there are no VM extensions the VM is still listed as running. So you have to wait for the forced shutdown. Not very nice but good enough.

I now declare this yak shaved!

blog comments powered by Disqus