Monday, November 30, 2015

Accessing a JSON webservice using libsoup and JSON-GLib

A lot of web services use the JSON format. If you are working a GLib based project and need to access a service like this there are two great libraries to help you - libsoup and JSON-Glib.

For my example, I'm going to grab some review data from Ubuntu (API) which looks something like this:

[
    {
        "ratings_total": 229,
        "ratings_average": "3.84",
        "app_name": "",
        "package_name": "simple-scan",
        "histogram": "[35, 13, 22, 42, 117]"
    },
    {
        "ratings_total": 546,
        "ratings_average": "4.66",
        "app_name": "",
        "package_name": "audacity",
        "histogram": "[17, 7, 17, 63, 442]"
    },

    ...
]

The data is a single array of objects that contain the statistics for each package. For this example I'll print out the number of ratings for each package by getting the package_name and ratings_total members from each object.

Firstly, I need to download the data. The data is retrieved using a HTTP GET request; in libsoup you can do this with:

    SoupSession *session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, "test-json", NULL);
    SoupMessage *message = soup_message_new (SOUP_METHOD_GET, "https://reviews.ubuntu.com/reviews/api/1.0/review-stats/any/any");

    soup_session_send_message (session, message);

Now I have the server text in message->response_body->data but it needs to be decoded. JSON-GLib can parse it with:

    JsonParser *parser = json_parser_new ();
    json_parser_load_from_data (parser, message->response_body->data, -1, NULL);
    JsonNode *root = json_parser_get_root (parser);


Now I have an in-memory tree of the JSON data which can be traversed. After checking the root node is an array as expected I'll iterate over each object:

    g_assert (JSON_NODE_HOLDS_ARRAY (root));
    array = json_node_get_array (root);
    for (i = 0; i < json_array_get_length (array); i++)
    {
        JsonNode *node = json_array_get_element (array, i);

        /* do stuff... */
    }

For each object, I extract the required data:

        g_assert (JSON_NODE_HOLDS_OBJECT (node));
        JsonObject *object = json_node_get_object (node);
        const gchar *package_name = json_object_get_string_member (object, "package_name");
        gint64 ratings_total = json_object_get_int_member (object, "ratings_total");
        if (package_name)
            g_print ("%s: %" G_GUINT64_FORMAT "\n", package_name,

 
Combined into a program, I can print out the number of reviews for each package:

simple-scan: 229
audacity: 546
...


The full program:

// gcc -g -Wall example-json.c -o example-json `pkg-config --cflags --libs libsoup-2.4 json-glib-1.0`

#include
#include

int main (int argc, char **argv)
{
    SoupSession *session;
    SoupMessage *message;
    guint status_code;
    JsonParser *parser;
    gboolean result;
    JsonNode *root;
    JsonArray *array;
    gint i;

    /* Get the data using a HTTP GET */
    session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, "test-json", NULL);
    message = soup_message_new (SOUP_METHOD_GET, "https://reviews.ubuntu.com/reviews/api/1.0/review-stats/any/any");
    g_assert (message != NULL);
    status_code = soup_session_send_message (session, message);
    g_assert (status_code == SOUP_STATUS_OK);

    /* Parse the data in JSON format */
    parser = json_parser_new ();
    result = json_parser_load_from_data (parser, message->response_body->data, -1, NULL);
    g_assert (result);

    /* The data should contain an array of JSON objects */
    root = json_parser_get_root (parser);
    g_assert (JSON_NODE_HOLDS_ARRAY (root));
    array = json_node_get_array (root);
    for (i = 0; i < json_array_get_length (array); i++)
    {
        JsonNode *node;
        JsonObject *object;
        const gchar *package_name;
        gint64 ratings_total;

        /* Get the nth object, skipping unexpected elements */
        node = json_array_get_element (array, i);
        if (!JSON_NODE_HOLDS_OBJECT (node))
            continue;

        /* Get the package name and number of ratings from the object - skip if has no name */
        object = json_node_get_object (node);
        package_name = json_object_get_string_member (object, "package_name");
        ratings_total = json_object_get_int_member (object, "ratings_total");
        if (package_name)
            g_print ("%s: %" G_GINT64_FORMAT "\n", package_name, ratings_total);
    }

    /* Clean up */
    g_object_unref (session); 
    g_object_unref (message);
    g_object_unref (parser);

    return 0;
}


And to show you can do the same thing with GIR bindings, here's the same in Vala:

// valac example-json.vala --pkg soup-2.4 --pkg json-glib-1.0

public int main (string[] args)
{
    /* Get the data using a HTTP GET */
    var session = new Soup.Session.with_options (Soup.SESSION_USER_AGENT, "test-json");
    var message = new Soup.Message ("GET", "https://reviews.ubuntu.com/reviews/api/1.0/review-stats/any/any");
    assert (message != null);
    var status_code = session.send_message (message);
    assert (status_code == Soup.Status.OK);

    /* Parse the data in JSON format */
    var parser = new Json.Parser ();
    try
    {
        parser.load_from_data ((string) message.response_body.data);
    }
    catch (Error e)
    {
    }

    /* The data should contain an array of JSON objects */
    var root = parser.get_root ();
    assert (root.get_node_type () == Json.NodeType.ARRAY);
    var array = root.get_array ();
    for (var i = 0; i
    {
        /* Get the nth object, skipping unexpected elements */
        var node = array.get_element (i);
        if (node.get_node_type () != Json.NodeType.OBJECT)
            continue;

        /* Get the package name and number of ratings from the object - skip if has no name */
        var object = node.get_object ();
        var package_name = object.get_string_member ("package_name");
        var ratings_total = object.get_int_member ("ratings_total");
        if (package_name != null)
            stdout.printf ("%s: %" + int64.FORMAT + "\n", package_name, ratings_total);
    }

    return 0;
}

and Python:

#!/usr/bin/python

from gi.repository import Soup
from gi.repository import Json

# Get the data using a HTTP GET
session = Soup.Session.new ()
session.set_property (Soup.SESSION_USER_AGENT, "test-json")
message = Soup.Message.new ("GET", "https://reviews.ubuntu.com/reviews/api/1.0/review-stats/any/any")
assert (message != None)
status_code = session.send_message (message)
assert (status_code == Soup.Status.OK)

# Parse the data in JSON format
parser = Json.Parser ()
parser.load_from_data (message.response_body.data, -1)

# The data should contain an array of JSON objects
root = parser.get_root ()
assert (root.get_node_type () == Json.NodeType.ARRAY)
array = root.get_array ()
for i in xrange (array.get_length ()):
    # Get the nth object, skipping unexpected elements
    node = array.get_element (i)
    if node.get_node_type () != Json.NodeType.OBJECT:
        continue

    # Get the package name and number of ratings from the object - skip if has no name
    object = node.get_object ()
    package_name = object.get_string_member ("package_name")
    ratings_total = object.get_int_member ("ratings_total")
    if package_name != None:
        print ("%s: %d" % (package_name, ratings_total))

Sunday, February 22, 2015

I wrote some more apps for Ubuntu Phone

In a previous post I talked about the experiences of writing my first five applications for Ubuntu Phone. Since then, I've written three more.

As before, all these apps are GPL 3 licensed and available on Launchpad. What's new is now you can browse them online due to a great unofficial web appstore made by Brian Douglass. This solves one of my previous gripes about not being able to find new applications.

One thing I have changed is I've started to use the standard page header. This makes it easy to add more pages (e.g. help, high scores). I initially wasn't a fan of the header due to the amount of space it took up but for simple apps it works well and it makes them predictable to use. I also went back and updated Dotty to use this style.

Let me introduce...


It's a simple utility to simulate dice rolling. Not much more to say. 431 lines of QML.


I was playing with uTorch and watching a spy movie and thinking "hey, wouldn't it be cool to flash the camera and send morse code messages. I wonder if the phone responds fast enough to do that." Apparently it does. 584 lines of QML.


So I thought, now I have this nice dice code from Dice Roller, it would be cool to make a full dice game with it. I've played a fair bit of Yahtzee in my time and searching Wikipedia I found there was a similar public domain game called Yatzy. 999 lines of QML.

Saturday, December 13, 2014

Tips for contributing code to open source projects

I've spent a lot of time over the years contributing to and reviewing code changes to open source projects. It can take a lot of work for the submitter and reviewer to get a change accepted and often they don't make it. Here are the things in my experience that successful contributions do.

Use the issue tracker. Having an open issue means there is always something to point to with all the history of the change that wont get lost. Submit patches using the appropriate method (merge proposals, pull requests, attachments in the issue tracker etc).

Sell your idea. The change is important to you but the maintainers may not think so. You may be a 1% use case that doesn't seem worth supporting. If the change fixes a bug describe exactly how to reproduce the issue and how serious it is. If the change is a new feature then show how it is useful.

Always follow the existing coding style. Even if you don't like it. If the existing code uses tabs, then use them too. Match brace style. If the existing code is inconsistent, match the code nearest to the changes you are making.

Make your change as small as possible. Put yourself in the mind of the reviewer. The longer the patch the more time it will take to review (and the less appealing it will be to do). You can always follow up later with more changes. First time contributors need more review - over time you can propose bigger changes and the reviewers can trust you more.

Read your patch before submitting it. You will often find bits you should have removed (whitespace, unrelated variable name changes, debugging code).

Be patient. It's OK to check back on progress - your change might have be forgotten about (everyone gets busy). Ask if there's any more you can do to make it easier to accept.

Thursday, November 27, 2014

Writing applications for Ubuntu Phone

I've just released my fifth application for the Ubuntu phone and I thought I'd do a write up of my experiences developing for Ubuntu Phone. In summary, it's been pretty positive!

The good:
  • Installing the SDK is as easy as installing any application in Ubuntu.
  • Writing applications is fast. You can throw together something fairly nice in a few hours.
  • Click packages are so easy to build! It makes .deb packages feel like something from the 1990s. Which is appropriate, because they are from the 1990s.
  • The deployment process is incredibly fast. You create a click package from the SDK, upload it to the store in a web form and it lands on my (or anyone else's) phone in under a minute normally. A freaking minute! That's amazing!
The bad / ugly:
  • The Ubuntu SDK (aka Qt Creator) still reinforces why I don't like IDEs. While it's better than older IDEs it's still overly complicated and cluttered with buttons. I only use it to dogfood the process and the command line tools aren't great for building and deploying applications (yet).
  • QML is... OK. It has all the technology of a modern toolkit (e.g. transitions, it's declarative, you can develop using a dynamic language) which is good. But it feels like it was put together in a rush. It's often not clear what the best way is to solve a problem and some components seem to be missing useful functionality (e.g. containers).
  • Javascript is great for small applications but quickly becomes unwieldy for large ones. The default other option is to use C++ which is just an enormous step backwards into complexity. I haven't yet tried Go QML but hopefully that will be a better combination.
  • The Ubuntu store interface is very basic. There's no way to list apps by ranking, you can't see new applications, there's no web interface. I'm sure it will get better soon but it's currently hard to find what's available (which is a big part of why I'm writing this blog post).
Here's what I've made; all these applications are released under the GPL 3 license and available on Launchpad. You can get the source for any of them by typing "bzr branch lp:euchre" from an Ubuntu machine.

Euchre


My first Ubuntu phone application. It's a classic four player trick taking card game with a basic AI.  I learnt a lot about animation in QML developing this. It's all written in Javascript which is really pushing the limits of maintainability for an application like this (1833 lines of QML). While it is the oldest it is also the least downloaded of my applications I think because Euchre is a bit of a niche game and I don't have any in game help.

Animal Farm


The inspiration for this was my daughter enjoying applications like this on Android. You touch the animals and they shake and meow / baa etc. It's trivially small (157 lines of QML).

Dotty


Dotty is a clone of the very successful iOS / Android game Dots. I thought I'd see if copying a popular game would transfer into success in Ubuntu and it has. This is my most popular game with 362 users currently compared to 160 for Animal Farm which is the next most popular. I learnt how to do dynamic components (i.e. the lines and the dots falling down) with this. A good size at 605 lines of QML.

Five Letters


Like Dotty I was looking for the type of games that are already popular on existing platforms. Word games are quite successful and I was thinking of games like 7 little words when designing this. The "making words from five letters" is a common newspaper game. I spent a lot of time trimming the dictionary of possible games to remove anything offensive or obscure so it should be reasonably possible to solve all the puzzles (there's about 1300 of them). 406 lines of QML.

Pairs


My newest game! Released last night. Like Animal Farm I was thinking of something my children might like to play. You turn over the cards two at a time and try and find the matching colours. The colours I've used actually make it quite difficult and fun to play as an adult. 409 lines of QML.

Monday, September 22, 2014

Using EGL with GTK+

I recently needed to port some code from GTK+ OpenGL code from GLX to EGL and I couldn't find any examples of how to do this. So to seed the Internet here is what I found out.

This is the simplest example I could make to show how to do this. In real life you probably want to do a lot more error checking. This will only work with X11; for other systems you will need to use equivalent methods in gdk/gdkwayland.h etc. For anything modern you should probably use OpenGL ES instead of OpenGL - to do this you'll need to change the attributes to eglChooseConfig and use EGL_OPENGL_ES_API in eglBindAPI.

Compile with:
gcc -g -Wall egl.c -o egl `pkg-config --cflags --libs gtk+-3.0 gdk-x11-3.0` -lEGL -lGL

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <EGL/egl.h>
#include <GL/gl.h>

static EGLDisplay *egl_display;
static EGLSurface *egl_surface;
static EGLContext *egl_context;

static void realize_cb (GtkWidget *widget)
{
    EGLConfig egl_config;
    EGLint n_config;
    EGLint attributes[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
                            EGL_NONE };

    egl_display = eglGetDisplay ((EGLNativeDisplayType) gdk_x11_display_get_xdisplay (gtk_widget_get_display (widget)));
    eglInitialize (egl_display, NULL, NULL);
    eglChooseConfig (egl_display, attributes, &egl_config, 1, &n_config);
    eglBindAPI (EGL_OPENGL_API);
    egl_surface = eglCreateWindowSurface (egl_display, egl_config, gdk_x11_window_get_xid (gtk_widget_get_window (widget)), NULL);
    egl_context = eglCreateContext (egl_display, egl_config, EGL_NO_CONTEXT, NULL);
}

static gboolean draw_cb (GtkWidget *widget)
{
    eglMakeCurrent (egl_display, egl_surface, egl_surface, egl_context);

    glViewport (0, 0, gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_height (widget));

    glClearColor (0, 0, 0, 1);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glOrtho (0, 100, 0, 100, 0, 1);

    glBegin (GL_TRIANGLES);
    glColor3f (1, 0, 0);
    glVertex2f (50, 10);
    glColor3f (0, 1, 0);
    glVertex2f (90, 90);
    glColor3f (0, 0, 1);
    glVertex2f (10, 90);
    glEnd ();

    eglSwapBuffers (egl_display, egl_surface);

    return TRUE;
}

int main (int argc, char **argv)
{
    GtkWidget *w;

    gtk_init (&argc, &argv);

    w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_double_buffered (GTK_WIDGET (w), FALSE);
    g_signal_connect (G_OBJECT (w), "realize", G_CALLBACK (realize_cb), NULL);
    g_signal_connect (G_OBJECT (w), "draw", G_CALLBACK (draw_cb), NULL);

    gtk_widget_show (w);

    gtk_main ();

    return 0;
}

Thursday, June 19, 2014

GTK+ applications in Unity 8 (Mir)

Ryan Lortie and I have been tinkering away with making getting GTK+ applications to run in Unity 8 and as you can see below it works!


This shows me running the Unity 8 preview session. Simple Scan shows up as an option and can be launched and perform a scan.

This is only a first start, and there's still lots of work to be done. In particular:

  • Applications need to set X-Ubuntu-Touch=true in their .desktop files to show in Unity 8.
  • Application icons from the gnome theme do not show (bug).
  • GTK+ applications don't go fullscreen (bug).
  • No cursors changes (bug).
  • We only support single window applications because we can't place/focus the subwindows yet (bug). We're currently faking menus and tooltips by drawing them onto the same surface.

If you are using Ubuntu 14.10 you can install the packages for this from a PPA:

$ sudo apt-add-repository ppa:ubuntu-desktop/gtk-mir
$ sudo apt-get update
$ sudo apt-get upgrade

The PPA contains a version of GTK+ with Mir support, fixes for libraries that assume you are running in X and a few select applications patched so they show in Unity 8.

The Mir backend currently on the wip/mir branch in the GTK+ git repository. We will keep developing it there until it is complete enough to propose into GTK+ master. We have updated jhbuild to support Mir so we can easily build and test this backend going forward.

Friday, May 09, 2014

errors.ubuntu.com

One of the most useful tools I use is errors.ubuntu.com. This shows statistics for the crash reports that are sent from users machines. Before I would trawl through Launchpad trying to work out which crash reports were more significant. Now we have numbers to see what's important.

Here is a graph showing the crash reports received for the last six releases over the last year:
Some interesting things from the graph:

  • We get more crash reports on weekdays than weekends (see the ripple in the 12.04 and 12.10 lines which otherwise seem quite stable).
  • Apparently people stop using Ubuntu development releases around Christmas (huge dip in the middle of the Ubuntu 14.04 line). Stable releases are unaffected.
  • You can see a step in crash reports for the 14.04 beta release (March). Looks like there was an outage then too as every release has a dip in reports before.
  • It looks like as soon as 14.04 was released (April) there's been a rapid migration from 13.10 users to 14.04 so there is now probably less than half the number of 13.10 users there were before.
  • If you look closely you can also see a slight decrease in crash reports from 12.04 after the 14.04 release, so people are migrating LTS to LTS.
  • I guess the 12.10 users love it because they don't seem to have started migrating at all.
  • We get a huge number of crash reports from release days and these very smoothly drop off over approximately three months. I guess this is due to the bugs being fixed and the users slowly updating.
  • Sorry, I don't get the vertical axis any more than you do other than to say "bigger means more crash reports" (bug). The X axis also show months from 2013/2014 (bug).
  • Not sure why the left hand side is so high - have we really reduced crash reports that much?

Sunday, March 23, 2014

Why the display server doesn't matter

Display servers are the component in the display stack that seems to hog a lot of the limelight. I think this is a bit of a mistake, as it's actually probably the least important component, at least to a user.

In the modern display stack there are five main components:
  • Hardware
  • Driver
  • Display Server / Shell
  • Toolkit / Platform API
  • Applications
The hardware we have no control over. We just get to pick which hardware to buy. The driver we have more control over - drivers range from completely closed source to fully open source. There's a tug of war between the hardware manufacturers who are used to being closed (like their hardware) and the open source community which wants to be able to modify / fix the drivers.

For (too) many years we've lived with the X display server in the open source world. But now we are moving into next generation display servers (as Apple and Microsoft did many years ago). At the moment there are two new classes of contender for X replacement, Mir and a set of Wayland based compositors (e.g. weston, mutter-wayland etc).

Applications use toolkits and platform APIs to access graphical functionality. There are plenty of toolkits out there (e.g. GTK+, Qt) and existing libraries are growing more broad, consistent and stable to be considered as a complete platform API (which is great for developers).

If you read the Internet you would think the most important part in this new world is the display server. But actually it's just a detail that doesn't matter that much.
  • Applications access the display server via a toolkit. All the successful toolkits support multiple backends because there's more than one OS out there today. In general you can take a GTK+ application and run it in Windows and everything just works.
  • The hardware and drivers are becoming more and more generic. Video cards used to have very specialised functionality and OpenGL used to provide only a fixed function function. Now video cards are basically massively parallel processors (see OpenCL) and OpenGL is a means of passing shaders and buffer contents.
The result of this is the display server doesn't matter much to applications because we have pretty good toolkits that already hide all this information from us. And it doesn't matter much to drivers as they're providing much the same operations to anything that uses them (i.e. buffer management and passing shaders around).

So what does matter now?
  • It does matter that we have open drivers. Because there will be different things exercising them we need to be able to fix drivers when display server B hits a bug but A doesn't. We saw this working with Mir on Android drivers. Since these drivers are only normally used by SurfaceFlinger there are odd bugs if you do things differently. Filing a bug report is no substitute to being able to read and fix the driver yourself.
  • The shell matters a lot more. We're moving on from the WIMP paradigm. We have multiple form factors now. The shell expresses what an application can do and different shells are likely to vary in what they allow.
I hope I've given some insight into the complex world of display stacks and shown we have plenty of room for innovation in the middle without causing major problems to the bits that matter to users. 

Tuesday, March 12, 2013

Ubuntu GNOME

Thanks to the hard work of Jeremy Bicha and others Ubuntu GNOME is now an official Ubuntu flavour. Flavours get some infrastructure and support benefits such as ISO creation that make it easier to release and support.

More information on Mir

With the recent announcement of Mir there's been some concern about what this means for Ubuntu and the wider Linux ecosystem. Christopher Halse Rogers who is on the Mir team has written some excellent posts covering some of the major questions: why Mir and not Wayland/Weston, what does this mean for other desktops on Ubuntu and what does this mean for Linux graphics drivers.

Well worth the read.

Tuesday, March 05, 2013

Mir

Today we go public with the Ubuntu graphics stack for the post X world. Since the beginning Ubuntu has relied on the X server to support the user experience and while it has worked generally well; it’s time for something new. My team is working on a big new component for this - Mir. Mir is a graphics technology that allows us to implement user experience we want for Ubuntu across all devices we support.

In many ways, Mir will be completely transparent to the user. Applications that use toolkits (e.g. Qt, GTK+) will not need to be recompiled. Unity will still look like Unity. We will support legacy X applications for the foreseeable future.

This is a big task. A lot of work has already been done and there’s a lot more to go. We’re aiming to do incremental improvements, and you can find more about this on the Wiki page and in the blueprints. You can help. From today our project is public, it’s GPL licensed and you’re welcome to use the source and propose changes.

It’s exciting times, and I hope you enjoy the results of this work!

Thursday, January 10, 2013

Vala support for Protocol Buffers

Recently I was playing around with Protocol Buffers, a data interchange format from Google. In the past I have spent quite a bit of time working with ASN.1 which is a similar format that has been around for many years. Protocol buffers seem to me to be a nice distillation of the useful parts of efficient data interchange and a welcome relief to the enormous size of the ASN.1 specifications.

With Vala being my favourite super-productive language I felt the need to add support to it. Solution: protobuf-vala.

Let's see it in action. Say you have the following protocol in rating.proto:

message Rating {
  required string thing = 1;
  required uint32 n_stars = 2 [default = 3];
  optional string comment = 3;
}


Run it through the protocol buffer compiler with:

$ protoc rating.proto --vala_out=.

This will create a Vala file rating.pb.vala with a class like this:

public class Rating
{

  string thing;
  uint32 n_stars;
  string comment;
  etc
}


You can use this class to encode a rating, e.g. for storing to a file or sending over a network protocol:

var rating = new Rating ();
rating.thing = "Vala";
rating.comment = "Vala is super awesome!";
rating.n_stars = 5;
var buffer = new Protobuf.EncodeBuffer ();
rating.encode (buffer);
do_something_with_data (buffer.data);

And decode it:

var data = get_data_from_somewhere ();
var buffer = new Protobuf.DecodeBuffer (data);
var rating = new Rating ();
rating.decode (buffer);
stderr.printf ("%s is %d stars\n", rating.thing, rating.n_stars);

That's pretty much it!

If you're using Ubuntu (12.04 LTS, 12.10 or 13.04) then you can install Vala protocol buffer support with:

$ sudo apt-add-repository ppa:protobuf-vala-team/ppa
$ sudo apt-get update
$ sudo apt-get install protobuf-compiler-vala libprotobuf-vala-dev

Have fun!

Thursday, December 27, 2012

A script for supporting multiple Ubuntu releases in a PPA

Something I find time consuming is uploading to a PPA when you want to support multiple Ubuntu releases. For my projects I generally want to support the most recent LTS release, the current stable release and the current development release (precise, quantal and raring when this was written).

I have my program in a branch and release that with make distcheck and lp-project-upload. The packaging is stored in another branch.

For each release I update the packaging with dch -i and add a new entry, e.g.

myproject (0.1.5-0ubuntu1) precise; urgency=low

  * New upstream release:
    - New exciting stuff

 -- Me <me@canonical.com>  Thu, 27 Dec 2012 16:52:22 +1300


I then run release.sh and this generates three source packages and uploads them to the PPA:

NAME=myproject
PPA=ppa:myteam/myproject
RELEASES="raring quantal precise"

VERSION=`head -1 debian/changelog | grep -o '[0-9.]*' | head -1`
ORIG_RELEASE=`head -1 debian/changelog | sed 's/.*) \(.*\);.*/\1/'`
for RELEASE in $RELEASES ;
do
  cp debian/changelog debian/changelog.backup
  sed -i "s/${ORIG_RELEASE}/${RELEASE}/;s/0ubuntu1/0ubuntu1~${RELEASE}1/" debian/changelog
  bzr-buildpackage -S -- -sa
  dput ${PPA} ../${NAME}_${VERSION}-0ubuntu1~${RELEASE}1_source.changes
  mv debian/changelog.backup debian/changelog
done


Hope this is useful for someone!

Note I don't use source recipes as I want just a single package uploaded for each release.

Wednesday, November 21, 2012

Testing Kerberos in Ubuntu

In fixing a LightDM bug recently I needed to set up Kerberos authentication for testing. Now, Kerberos comes with quite a reputation for complexity so this was not a task I was looking forward to. And googling around to get some simple Ubuntu instructions only ended up confirming my expectations. But in the end, I was able to get it to work [1] and here is what I did. You should probably not rely on this information for an actual Kerberos implementation.

I start with two machines running Ubuntu, one as the Kerberos server [2] and one as a client. The client is already installed with a user account called test.

Server configuration


Edit /etc/krb5.conf to set the default realm [3]:
 
[libdefaults]
default_realm = TEST

Install the Kerberos server:

$ sudo apt-get install krb5-kdc krb5-admin-server

Create the realm. You will be prompted for a master password for the realm:

$ sudo krb5_newrealm

Add a new user (called a principal in Kerberos language) into the realm with the same username as on the client. You will be prompted for a password for this user [4]:

$ sudo kadmin.local
kadmin.local:  add_principal test


And now the server should be running. You can check things are working by watching the log:

$ tail -f /var/log/auth.log

Client configuration


The client is a lot easier, as the packages do most of the work for you:

$ sudo apt-get install krb5-user

You will be prompted for the following information:
  • Set "Default Kerberos version 5 realm" to TEST
  • Set "Kerberos server for your realm" to address / hostname of your server
  • Set "Administrative server for your Kerberos realm" to address / hostname of your server
Now you can test by getting a ticket [5] from the server. You will be prompted for the password you set when running kadmin.local on the server:

$ kinit
$ kdestroy


If that worked then you're ready to go. Have a look at the auth.log on the sever if it didn't work (the error messages are a bit cryptic though).

The next step is to setup PAM [6] to allow authentication with Kerberos. There's no configuration required, just install it:

$ sudo apt-get install libpam-krb5

Now you can log into your client machine (e.g. from LightDM/Unity Greeter) using the Kerberos password you setup on the server. Remember if something went wrong you can still use the local password to get in [7].

The reason I set all this up was to test Kerberos accounts which need password changes. You can control this feature from the server using the following:

$ sudo kadmin.local
kadmin.local:  modify_principal +needchange test



[1] on Ubuntu 13.04 (server) and 12.04 (client). I don't know which other combinations will work.
[2] Called a Key Distribution Centre in Kerberos jargon.
[3] Kerberos calls different authentication domains realms. I've used the realm TEST though in proper usage this would be a domain name e.g. EXAMPLE.COM to avoid name collision.
[4] You will already have a password set for this user on the client machine. Pick a different password as this allows you log in with either Kerberos or local passwords - both passwords will work.
[5] A ticket is the name for an authentication token provided by the server. In a real implementation this ticket will allow you to access services without re-entering your password.
[6] PAM is the library that does authentication when logging into Ubuntu.
[7] The PAM configuration that the packages setup first tries your password with the Kerberos server, then the local passwords (/etc/shadow) if that fails.

Thursday, February 09, 2012

So You Want to Write a LightDM Greeter…

Matt Fischer wrote a great post about writing a greeter for LightDM.  Runs through an example of a Python greeter and how it works.

Thursday, December 08, 2011

Gnome Games Modernisation

The GNOME Games project maintains fifteen small "five-minute" games for the GNOME desktop.

Unfortunately over time the games have struggled to keep up with the latest GNOME technology due to the time required to do this.  And the further behind we've got the harder it is for new developers to get involved as the code is hard to work with.

So the time has come for a great modernising.  And here's where you fit it :)

We've picked eight of the games we think are the best and we want to focus on bringing them up to modern standards.  The games are Chess, Five or More, Mines, Iagno, Mahjongg, Sudoku and Swell Foop.  These games all have been or are in progress of being ported to Vala.  Vala is a modern programming language that will be familiar to anyone who has used Java or C#.

We have a Matrix of things to do:
with the goal being to turn everything to green.


So, if you're interested in helping out follow any of the bug links and start fixing the bugs!  All the tasks should be able to be completed independently and shouldn't be too complex to achieve.  Anyone is welcome to attempt these and there are non-coding tasks (documentation and design).

Saturday, September 10, 2011

GNOME OS

There was a very good interview with Jon McCann recently about GNOME 3 and GNOME OS.  Reading public comments on this interview showed a lot of negativity which I think missed the good points.

GNOME OS is unfortunately very loosely defined, but from what I can gather it's essentially controlling the entire stack that GNOME is to make a better experience and make it easier to work on the project.

What I like about this strategy:
  • It's focuses on the users that GNOME is targeted at.  We've had a strong direction since GNOME 2 days and the focus on features that these users need through design is the right way of getting there.
  • It's dropping old desktop metaphors and moving to new ones.  There are other desktops like XFCE which will continue the Windows 95 desktop metaphor and be successful with it; it's right for GNOME to move on and be more cutting edge.
What I don't like about it:
  • It downplays the value of GNOME as a "box of bits".  The drum I'm banging at the moment is about sharing infrastructure.  This is something GNOME has been very successful with in the past and discouraging this cuts off a lot of places where GNOME can get investment from other projects.
  • It puts very strong requirements on distributors which they don't want to / can't meet.  GNOME is not like Apple, it can't control the entire stack from hardware to sales.  It needs to work with distributors or have a distribution strategy.  Building a perfect desktop is not enough.
The impression I get is GNOME OS is now effectively the strategy of GNOME and it's generally a good direction.  We need to make sure to flesh it out and ensure that we can have sustained development in GNOME and get wide distribution.

Desktop common ground

There's a common argument that you hear about open source desktops which goes something like "we have less than 1% market share; the other desktops are laughing at us; we should pool together and make a real contender".

And you know what, they're right!  We don't have a significant market share, and we're not at the point where we have a truly amazing desktop experience (but we're getting closer).  Anything we can do to get there faster must be a good thing.

And you know what, they're wrong.  We don't have a finite developer resource.  Open-source is amazing like that - when a project starts that people care about suddenly the community grows.  Trying to mash everyone together into one project wouldn't work and would probably make us even slower.  Putting all our eggs in one basket is a big risk.

How can we share resources to grow that market share without pushing us together into a big compromise?  We need to share infrastructure.  What we need is a POSIX for the 21st century.  We've been slowly building this with things like the Linux, D-Bus, X, GStreamer.  We can do more.

There's been a rise in design thinking in Open-Source which has been really good for everyone.  But I think the pendulum has swung too far.  User experience is not the only factor in deciding what to do.  Infrastructure is expensive.  Every bad API is slowing us down progress on the layers above it.  Every desktop developer that dives into infrastructure is not working on those layers either.

Sharing is hard.  But the cost of not sharing is huge.  Lets make sure the infrastructure we're building for tomorrow works cross-desktop and we can share those costs.

Friday, September 02, 2011

Desktop Summit 2011

Last month I attended the Desktop Summit 2011 in Berlin.  Unfortunately I was only there for the core days because Berlin is an awesome city and the summit is awesome too.

The quality of the talks this year were great, and I only had one or two slots the whole time where there was nothing I wanted to go to.  This summit felt more integrated than the last one and I hope this continues into the future.

Some highlights:
  • There was a good response to LightDM.  I felt my talk had a lack of GNOME people present, but I think the GTK4 talk may have absorbed them.
  • Lennart did a well researched talk on revamping the login system which sounds very good and left me wanting to know more.
  • From what I heard the future of GTK+4 and Clutter looks very promising, but I haven't been able to see the talk as I was doing mine.  Can't wait for the videos to come out so I can find out more.
  • Vincent Untz is did a really thoughtful talk on his experiences as a GNOME release manager.
  • GNOME Shell seems to be progressing very well and there were a lot of talks on it.
  • Plasma/KDE also seems to be doing a lot of innovation.
Some negatives:
  • There was basically no mention of Unity.
  • There was the usual amount of Canonical bashing and it's not helping anyone.  The GNOME State of the Union had too many cheap jabs and the half hearted laughter shows it's just not funny anymore.
  • There was a lack of Canonical people present, and it was commented on numerous times.  I'm personally not surprised, as every year more of my colleagues just don't want to be there.  Andrea Cimitan, who is a great guy, summed it up when he said on Google+ "when I say around here I'm working for "Canonical", people stop smiling :)".
  • There was little mention of GNOME OS.  Sometimes we need to be more than just hackers talking about technology and really talk more about planning and strategy.
What I'd like to see at the next summit:
  • Increased visibility of other desktops - it still feels very GNOME and KDE centric, I think we can learn a lot from projects like XFCE, LXDE, Elementary, Unity etc.
  • Increased collaboration on infrastructure - we need to get freedesktop.org in a better shape so we can pool out resources on the boring stuff and focus more on the user facing component which make us successful.

Thursday, September 01, 2011

Broken PDFs in Simple Scan

Since version 2.32 Simple Scan has had a bug where it generates PDF files with invalid cross-reference tables.  The good news is this bug is now fixed, and will work correctly in simple-scan 3.2; thanks to Rafał Mużyło who diagnosed this.  You may not have noticed this bug as a number of PDF readers handle these types of failures and rebuild the table (e.g. Evince).  It was noticed that some versions of Adobe Reader do not handle these failures.

I've added a command line option that can fix existing PDF files that you have generated with Simple Scan.  To use run the following:

simple-scan --fix-pdf ~/Documents/*.pdf

It should be safe to run this on all PDF documents but PLEASE BACKUP FIRST. It will copy the existing document to DocumentName.pdf~ before replacing it with the fixed version so you have those in case anything goes wrong.

If you can't wait for the next simple-scan, you can also run this Python program (i.e. python fixpdf.py broken.pdf > fixed.pdf)

import sys
import re
lines = file (sys.argv[1]).readlines ()
xref_offset = int(lines[-2])
xref_offset = 0
for (n, line) in enumerate (lines):
        # Fix PDF header and binary comment
        if (n == 0 or n == 1) and line.startswith ('%%'):
                xref_offset -= 1
                line = line[1:]
        # Fix xref format
        match = re.match ('(\d\d\d\d\d\d\d\d\d\d) 0000 n\n', line)
        if match != None:
                offset = int (match.groups ()[0])
                line = '%010d 00000 n \n' % (offset + xref_offset)
        # Fix xref offset
        if n == len(lines) - 2:
                line = '%d\n' % (int (line) + xref_offset)
        # Fix EOF marker
        if n == len(lines) - 1 and line.startswith ('%%%%'):
            line = line[2:]
        print line,