Saturday, February 4, 2012

Creating application with background daemon with QtCreator

This posting outlines how to create a program to run in the background (daemon) in MeeGo Harmattan with QtCreator. The resulting installation file is a single .deb that can be submitted to Nokia Store. The daemon will be started after installing the application, and will be started upon each reboot of the device.

The tutorial assumes you want to create a QML application that can be used to control the background daemon, and the daemon itself. The name of the app will be exampleapp.

Quick outline

This is a quick outline of what has to be done, with details in later chapters:
  • Create in QtCreator a subdirs project.
  • Inside the subdirs project create a console application project for the daemon and a Qt Quick Application project for the UI application.
  • Create a configuration file that controls launching of the daemon.
  • Create aegis manifest file to set credentials for daemon.
  • Create debian post-installation and before removal scripts to start the daemon after installation, and stopping the daemon before uninstallation.

Creating the basic project structure in QtCreator

The applications submitted to Nokia store should be contained in single .deb file. To have an application that contains two binaries - the daemon and the application providing the graphical user interface - we need two different projects. We can have both a single .deb file and two different projects by creating a subdirs project.

The final project will have a file structure like this:

- exampleapp/
  - exampleapp/
    - exampleapp*.png
    - exampleapp_harmattan.desktop
    - exampleapp.pro
    - main.cpp
    - qml/
  exampleappd/
    - exampleappd.conf
    - exampleappd.pro
    - main.cpp
  qtc_packaging/
    - debian_harmattan/
      - manifest.aegis
      - ... + various other files

Select File - New File or Project - Other Project - Subdirs project. Give it a name, in this example it's named exampleapp. Select the compilation targets, of course at least including Harmattan. Then click Finish & Add Subproject. As you've selected Harmattan as a target, QtCreator then asks if it should include various debian packaging related files to the project. Click Yes.

Now QtCreator offers to create the first of the subprojects. We can start by creating the QML project first, although the order does not matter. Choose from the dialog Qt Quick project - Qt Quick Application. As its name give the same name as the main subdirs project's name, in this example the name will be exampleapp. Select as "Qt Quick Application Type" Qt Quick Components for Meego/Harmattan. In Target Setup dialog make sure at least Harmattan is selected.

After the graphical QML based project is created, it's time to create the daemon project. Right click on Projects view on the subdirs project and select New subproject. Choose as project template Other Project - Qt Console Application. Give it a name, in this example I enter exampleappd (postscript 'd' is a common unix convention for daemon processes). Again on Target Setup make sure Harmattan is selected.

After this you have two projects, one for the graphical user interface, and one for the daemon running in the background. You do what you do in each of them, such as communicating with each other via dbus (which is out of scope for this blog posting).

Creating daemon control file

In order for the daemon to be started automatically on device reboots, a .conf file that is installed under
/etc/init/apps
is needed. The configuration files in there are processed by Upstart (Google it for more information). Here's a bare bones version of it for the example. Create a file exampleapp/exampleappd/exampleappd.conf

description "ExampleApp daemon startup script"
author "harmarto@somewherecom"
stop on core_shutdown
console output
respawn
respawn limit 3 300
normal exit 0
# This tries executing the daemon as 'user', this is
# what's almost always wanted, but you also need
# aegis.manifest file for that to work (shown later).
exec /usr/bin/aegis-exec -s -u user /opt/exampleapp/bin/exampleappd

For more options in the configuration file and some explanations on the values used see these resources:

You need to add the file to to exampleapp/exampleappd/exampleappd.pro so that it will be installed to a place where Upstart finds it (you can add this to the end of the file):
 
daemonconf.path = /etc/init/apps
daemonconf.files = exampleapp.conf
INSTALLS += daemonconf 
As it is preferable that all your binaries are bundled in the same location (/opt/exampleapp/bin/), you need to also add this to exampleappd.pro:
target.path = /opt/exampleapp/bin
INSTALLS += target

Aegis manifest

On Harmattan you will almost always want to run the daemon as user, not as a root. On Harmattan the credentials your application requests and receives from the security platform are far more important than the traditional unix root/user division. As Upstart doesn't run as 'user', you will need to request credentials for your daemon to switch to running as user. QtCreator should already have created an empty file at exampleapp/qtc_packaging/debian_harmattan/manifest.aegis. Put the following into it to get your daemon running as user:
<aegis>
  <request>
    <credential name="UID::user" />
    <credential name="GID::users" />
    <for path="/opt/exampleapp/bin/exampleappd"/>
  </request>
</aegis>

Starting the daemon after installation

If you want the daemon to start after user installs your application, you need to add a script that is executed after successfull installation.

Create following file at exampleapp/qtc_packaging/debian_harmattan/postinst

#!/bin/sh
set -e

case "$1" in
    configure)

    echo "Starting exampleappd ..."
    [[ -e /etc/init/apps/exampleappd.conf ]] && initctl start apps/exampleappd
    ;;

    abort-upgrade|abort-remove|abort-deconfigure)
    ;;

    *)
        echo "postinst called with unknown argument \`$1'" >&2
        exit 1
    ;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0
the initstl start command is an Upstart command to execute the commands in your exampleappd.conf file.

Stopping the daemon after uninstalling

Even more important is to ensure that your daemon is stopped if user uninstalls your application. In a similar way to to running script after installing, you can instruct the system to run a script before uninstalling to stop the daemon.

Create following file exampleapp/qtc_packaging/debian_harmattan/prerm

#!/bin/sh

set -e

case "$1" in
    purge|remove|upgrade)
        initctl stop apps/exampleappd || true
        # Just in case Upstart could not stop the daemon, kill it    
        killall exampleappd || true        
    ;;
    
    failed-upgrade|abort-install|abort-upgrade|disappear)
    ;;

    *)
        echo "postrm called with unknown argument \`$1'" >&2
        exit 1
    ;;
esac

#DEBHELPER#

exit 0

Running from QtCreator

You can run the resulting project from QtCreator, but it is not guaranteed that your daemon starts before the user interface application. That can be slight annoyance depending on the project. Nevertheless, when you run the application with Harmattan target, the files are installed on your device. You can then, if needed, close the user interface application, start the daemon yourself from command line by running /opt/bin/exampleapp/bin/exampleappd, and then start the UI from the application grid.

Caveats

The daemon application must be pure console application. In other words, no graphics should be displayed by it. Trying to create any graphics context will most likely just result in abrupt termination of your daemon. There are simple ways to have daemons that can display graphics and QML, but that's a different topic if there's interest.

Thanks to

The postinst/prerm scripts are lifted pretty much straight from sandst1's MyMoves sources. Many thanks to his great application, as well as for sharing the sources.