code

Nagcat: a monitoring framework for Nagios

I am pleased to announce the first public release of Nagcat! This is a project I have been working on at ITA Software over the past several months for monitoring the complex applications we run. It is intended to be used with Nagios 3.x and is written in Python.

I just finished cutting the release and setting up its new project site and now I think it is time for a beer so this announcement is rather short. I'll be posting some HOWTOs and other information here in the future.

Class inheritence in C

For a while now I have been slowly developing a touchscreen music player to replace the stereo in my car. Although that concept should be fairly simple the project has turned into a bit of a playground for learning how to implement an X toolkit using nothing but C, XCB, and Cairo. One of the more recent legs this project has grown is an object oriented system for C which supports class inheritance and method overriding for use in my toolkit.

At this point I've got it almost the way I like it. Defining a class in a header looks something like this:

CLASS(mtk_widget, mtk_object)
        int x, y, w, h;
        struct mtk_window *window;
        struct mtk_widget *parent;
        cairo_surface_t *surface;
METHODS(mtk_widget, mtk_object, int x, int y, int w, int h)
        void (*init)(mtk_widget_t *this, mtk_widget_t* parent);
        void (*draw)(mtk_widget_t *this); /* children must implement this */
        void (*update)(mtk_widget_t *this);
        void (*mouse_press)(mtk_widget_t *this, int x, int y);
        void (*mouse_release)(mtk_widget_t *this, int x, int y);
        void (*mouse_move)(mtk_widget_t *this, int x, int y);
        void (*set_geometry)(mtk_widget_t *this, int x, int y, int w, int h);
        void (*set_parent)(mtk_widget_t *this, mtk_widget_t *parent);
END

This defines a new class named mtk_widget based on the base class mtk_object. The extra arguments to the METHODS macro are additional arguments for the object constructor.

To actually implement the class the corresponding C file will have something like this:

/* more methods up here */

static void set_parent(mtk_widget_t *this, mtk_widget_t *parent)
{
        this->parent = parent;
}

mtk_widget_t* mtk_widget_new(size_t size, int x, int y, int w, int h)
{
        mtk_widget_t* this = mtk_widget(mtk_object_new(size));

        SET_CLASS(this, mtk_widget);
        this->x = x;
        this->y = y;
        this->w = w;
        this->h = h;

        return this;
}

METHOD_TABLE_INIT(mtk_widget, mtk_object)
        METHOD(init);
        METHOD(draw);
        METHOD(set_geometry);
        METHOD(set_parent);
METHOD_TABLE_END

Methods can (and probably should) be static functions. The METHOD_TABLE_INIT macro defines the function _mtk_widget_class_init() to fill up a hidden structure named _mtk_widget_class which is the class virtual method table. Each object then has a pointer tucked away inside to its class's method table (set by the SET_CLASS macro). One thing that is a bit clunky about this is that the program must somehow call _mtk_widget_class_init() before the class is ever used. I would like to be able to do away with that by using GCC's constructor function attribute so it magically runs before main(). However the class hierarchy must be initialized in order but a way to order the constructors was not added until GCC 4.3. I'm using 4.1 on my system so that feature will have to wait for now.

Actually using objects looks something like this:

int main (int argc, char *argv[])
{
        mtk_window_t *window;
        mtk_widget_t *widget;

        mtk_init();

        window = new(mtk_window,640, 480);
        widget = mtk_widget(new(mtk_text, 0, 0, 640, 480, "WHEE"));
        call(window, mtk_container, add_widget, widget);

        mtk_main();

        mtk_cleanup();

        return 0;
}

Pretty strait forward, call() is a bit weird though. It takes the arguments (object, class name that defined the method, method name, method arguments). I would like to be able to get rid of the class argument somehow (it is used to cast object to the correct type) but I haven't come up with a solution yet. I have a similar clunkyness with my super() macro for calling a parent class's method when doing method overriding.

That's pretty much it. As with the rest of this project it is a good example of making things more complicated than they need to be but it was an interesting challenge to put together.

The code for the above macros and base class can be found in this header and c file.

Giving a chroot its own hostname with chname

Since 2.6.19 Linux has supported a really nifty little feature: utsname namespaces. This is meant for use in fancy container systems but can be useful for simple chroots as well. By creating a chroot in a new namespace the chroot can be given its own hostname. This can be useful for managing a chroot as an independent host or simply making it easy to see if you are in the chroot or not.

A while back I wrote a little tool called chname to make use of the feature when not using a fancy container system. It will start a new process, such as chroot, with a new hostname:

chname newhost chroot /chroots/newhost /bin/bash

And poof!

Hopefully someone else will also find this useful. For Gentoo users it is already in the Portage tree so just emerge it. :-)

Be sure to compile your kernel with CONFIG_UTS_NS=y

General setup  --->
  [*] UTS Namespaces

Update:
For those wondering "Why bother?" I originally wrote this to make running cfengine in a chroot easier. Our cfengine setup at the OSL configures systems based on hostname. Without changing the hostname cfagent must be run as:

cfagent -q -D newhost -D newhost_osuosl_org -N oldhost_osuosl_org

Which is kind of annoying sometimes. Also, thanks to a bug/feature in cfengine if a system hosts a chroot it must always be referred to in the cfengine config as the full oldhost_osuosl_org instead of the nice and shorter oldhost class. It is impossible to unset the class oldhost, but at least undefining oldhost_osuosl_org works. Maybe I'll fix cfengine some day so undefining oldhost works but I kinda like the chname method better.

Syndicate content