Skip to content
Snippets Groups Projects
Commit 4f533a52 authored by Ryan Gonzalez's avatar Ryan Gonzalez
Browse files

creating-a-canterbury-agent: Remove legacy document

Canterbury is no longer used, and the concept of "agents" no longer
applies.

https://phabricator.apertis.org/T8426



Signed-off-by: default avatarRyan Gonzalez <ryan.gonzalez@collabora.com>
parent 3c96837e
No related branches found
No related tags found
1 merge request!400Various documentation update for new appfw
......@@ -11,6 +11,7 @@ aliases = [
]
date = "2016-12-12"
lastmod = "2022-02-01"
status = "Deprecated"
statusDescription = "This document covers frameworks that have been removed in v2022dev2."
......@@ -65,7 +66,7 @@ It is likely that there will be times when access to the internet will be limite
An Apertis **application bundle** is a set of resources installed as a single
unit. The app bundle can include open source / proprietary libraries,
[agents][Agent] and GUI programs.
services, and GUI programs.
See the [Application Bundle Specification]( {{< ref "bundle-spec.md" >}} )
for a more formal specification of the contents of application bundles.
......@@ -103,10 +104,6 @@ For example, the `net.example.ShoppingList` application bundle might contain
a main entry point named `net.example.ShoppingList` and an [agent] named
`net.example.ShoppingList.CloudSync`.
<!-- Local link definitions -->
[Agent]: {{< ref "creating-a-canterbury-agent.md" >}}
<!-- External link definitions -->
[Reversed domain name notation]: https://en.wikipedia.org/wiki/Reverse_domain_name_notation
......@@ -10,13 +10,6 @@ aliases = [
]
+++
{{< glossary-term agent >}}
A persistent non-GUI process launched automatically at boot time, immediately
after application installation or by D-Bus activation.
See: [Creating a Canterbury agent]({{< ref "creating-a-canterbury-agent.md" >}})
{{< /glossary-term >}}
{{< glossary-term "application bundle" "app bundle" "bundle" >}}
A group of functionally related components (be they services, data, or
programs), installed as a unit. This matches the sense with which "app" is
......
+++
title = "Creating a Canterbury agent"
weight = 100
toc = true
aliases = [
"/old-developer/latest/appdev-agents.html",
"/old-developer/v2019/appdev-agents.html",
"/old-developer/v2020/appdev-agents.html",
"/old-developer/v2021pre/appdev-agents.html",
"/old-developer/v2022dev0/appdev-agents.html",
]
date = "2016-12-09"
lastmod = "2017-07-01"
+++
{{% notice warning %}}
This guide describes creating an agent using the
[Centerbury legacy application framework]({{< ref "canterbury-legacy-application-framework.md" >}}).
This framework has been removed in `v2022dev2` and therefore not recommended
for new development.
{{% /notice %}}
An agent is a non graphical program which runs in the background. Agents can be used for playing music or computing expensive operations like indexing large databases, for example.
Following is a step-by-step guide for creating an agent. This guide is based on the [agent sample application](https://git.apertis.org/git/sample-applications/helloworld-agentapp.git) available in the Apertis repos. It will be handy to have the sample application code open while reading through this guide.
Our sample application is composed of two classes which will be explained in more detail below:
- `HlwAgent`: A wrapper class that manages the process itself and the D-Bus object
- `HlwTickBoard`: A child class that implements the D-Bus interface
Let's get started!
# Agents are GApplications
The [`GApplication`](https://developer.gnome.org/gio/stable/GApplication.html) is the recommended base class for agents, just as it is for graphical applications.
One of the advantages of being derived from `GApplication` is that most of the work of registering with D-Bus is handled by the class, so you need only override a few virtual functions in order to get a running agent.
The `HlwAgent` class is the entry point for running the agent. This class is defined as:
```
G_DEFINE_TYPE (HlwAgent, hlw_agent, G_TYPE_APPLICATION)
```
Create and launch an `HlwAgent`:
```
app = G_APPLICATION (g_object_new (HLW_TYPE_AGENT,
"application-id", "org.apertis.HelloWorld.Agent",
"flags", G_APPLICATION_IS_SERVICE,
NULL));
g_application_run (app, argc, argv);
```
The `application_id` property of the GApplication is used to provide the D-Bus bus name. This id should be unique so it is recommend that you use `@BUDNLE_ID@.Agent`. `GApplicationFlags` should be set to `G_APPLICATION_IS_SERVICE`. See [the description](https://developer.gnome.org/gio/stable/GApplication.html#GApplicationFlags) of flags for more information.
In the `HlwAgent` class, the `dbus_register` and `dbus_unregister` virtual functions should be overridden to catch whether the requested D-Bus name is available to use, and`activate` should be overridden as well, which is a mandatory virtual function for all applications and agents.
```
static void
hlw_agent_class_init (HlwAgentClass *klass)
{
GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
...
app_class->activate = hlw_agent_activate;
app_class->dbus_register = hlw_agent_dbus_register;
app_class->dbus_unregister = hlw_agent_dbus_unregister;
...
}
```
To cause `HlwAgent` to run in the background and prevent termination by the ending of the main function, the reference count should be held and released properly. In a graphical application, it's clear that `g_application_hold ()` should be called when the window appears and `g_application_release ()` when the window disappears, but unlike a graphical program, it's a bit less clear when to manage reference count in an agent. Fortunately, in the `dbus_register` function, we can assume that the agent is ready to export extra objects on the bus, making it a good place to take the reference with `g_application_hold ()`, and in `dbus_unregister` we can release the reference count with `g_application_release ()`.
```
static gboolean
hlw_agent_dbus_register (GApplication *app,
GDBusConnection *connection,
const gchar *object_path,
GError **error)
{
...
g_application_hold (app);
...
}
static void
hlw_agent_dbus_unregister (GApplication *app,
GDBusConnection *connection,
const gchar *object_path)
{
...
g_application_release (app);
...
}
```
Once the `activate` virtual function is overridden, we will have a runnable agent skeleton.
# D-Bus Interface
Using the `GApplication` class allows our agent to run as stand-alone non-graphical program, but it isn't yet able to interact with the outside world. It is recommended that D-Bus be used to communicate with other processes on the system. D-Bus code and documentation can be easily created by `gdbus-codegen`. For more details of the usages of the generator, refer to [gdbus-codegen](https://developer.gnome.org/gio/stable/gdbus-codegen.html).
## D-Bus XML Schema
To make our agent application D-Bus aware, let's introduce a simple XML D-Bus interface.
Our interface will contain the following:
- method: ToggleTick
- property: CurrentTick
By calling `ToggleTick`, an auto-incremented tick count function is enabled or disabled. Once the function is enabled, the property, `CurrentTick`, is increased by 1 every second.
```
<node name="/org/apertis/helloworld/agent/tickboard">
<interface name="org.apertis.HelloWorld.Agent.TickBoard">
<method name="ToggleTick"/>
<property name="CurrentTick" type="i" access="read"/>
</interface>
</node>
```
To generate the D-Bus code at make time, we introduce a simple rule to the Automake `Makefile.am`.
```
helloworld-agent/%.c: helloworld-agent/%.xml Makefile
$(AM_V_GEN)$(GDBUS_CODEGEN) \
--c-namespace=HlwDBus \
--interface-prefix=org.apertis.HelloWorld.Agent \
--generate-c-code helloworld-agent/$* $<
```
Note that `--interface-prefix` should be the same as the `application-id` of the agent.
## D-Bus function implementation
The D-Bus skeleton is generated by `gdbus-codegen` according to the XML schema. Now we need to create actual behaviors for when the D-Bus APIs are called. In our example, the `HlwDBusTickBoardSkeleton` object and the `HlwDBusTickBoard` interface are created. Although there are various approaches to implementing this interface, being a child of the D-Bus skeleton object will help show how the generated virtual functions should be filled in.
```
G_DEFINE_TYPE_WITH_CODE (HlwTickBoard, hlw_tick_board,
HLW_DBUS_TYPE_TICK_BOARD_SKELETON,
G_IMPLEMENT_INTERFACE (HLW_DBUS_TYPE_TICK_BOARD,
hlw_tick_board_tick_board_iface_init))
```
Next, `handle_toggle_tick` of `HlwDbusTickBoardIface` should be overridden.
```
static gboolean
hlw_tick_board_handle_toggle_tick (HlwDBusTickBoard *object,
GDBusMethodInvocation *invocation)
{
...
hlw_dbus_tick_board_complete_toggle_tick (object, invocation);
return TRUE;
}
static void
hlw_tick_board_tick_board_iface_init (HlwDBusTickBoardIface *iface)
{
iface->handle_toggle_tick = hlw_tick_board_handle_toggle_tick;
}
```
## Exporting the interface to D-Bus
Once the implementation of the D-Bus interface is completed, it needs to be exported on the bus. `HlwAgent` is already registered on the bus so the `HlwTickBoard`, which is a child object of the D-Bus skeleton, can be easily exported in the `dbus_register` function.
```
static gboolean
hlw_agent_dbus_register (GApplication *app,
GDBusConnection *connection,
const gchar *object_path,
GError **error)
{
gboolean ret;
...
/* chain up */
ret = G_APPLICATION_CLASS (hlw_agent_parent_class)->dbus_register (
app,
connection,
object_path,
error);
if (ret &&
!g_dbus_interface_skeleton_export (
G_DBUS_INTERFACE_SKELETON (self->tick_board),
connection,
"/org/apertis/HelloWorld/Agent/TickBoard",
error))
{
g_warning ("Failed to export TickBoard D-Bus interface (reason: %s)",
(*error)->message);
}
return ret;
}
```
Note that it is recommended that an application registers as above before exporting any other interfaces to D-Bus.
# Apparmor profile
An agent must be granted different Apparmor permissions than those granted to standard [application bundles]( {{< ref "bundle-spec.md#apparmor-profile" >}} ). Several additional rules are required to allow an agent to access the bus.
```
/Applications/@BUNDLE_ID@/** {
#include <abstractions/chaiwala-base>
#include <abstractions/dbus-session-strict>
/Applications/@BUNDLE_ID@/{bin,libexec}/* pix,
/Applications/@BUNDLE_ID@/{bin,lib,libexec}/{,**} mr,
/Applications/@BUNDLE_ID@/share/{,**} r,
owner /var/Applications/@BUNDLE_ID@/users/** rwk,
dbus send
bus=session
path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member="RequestName"
peer=(name=org.freedesktop.DBus),
dbus bind
bus=session
name="@BUNDLE_ID@",
dbus bind
bus=session
name="@BUNDLE_ID@.Agent",
dbus (send, receive)
bus=session
peer=(label=/Applications/@BUNDLE_ID@/**),
signal receive peer=/usr/bin/canterbury,
}
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment