dunst/src/dbus.c
Benedikt Heine 7e92619967 Add ability to trigger action of latest notification
Currently triggers only the latest notification. TODO: implement it
either with ActionsAll or more favorable: with an optional paramter,
which should trigger only the action of a single notification.

The problem currently: I have got no ability to check the DBus docs, as
I'm on a bad internet connection.
2020-05-01 15:35:23 +02:00

863 lines
32 KiB
C

/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
#include "dbus.h"
#include <gio/gio.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include "dunst.h"
#include "log.h"
#include "menu.h"
#include "notification.h"
#include "queues.h"
#include "settings.h"
#include "utils.h"
#define FDN_PATH "/org/freedesktop/Notifications"
#define FDN_IFAC "org.freedesktop.Notifications"
#define FDN_NAME "org.freedesktop.Notifications"
#define DUNST_PATH "/org/freedesktop/Notifications"
#define DUNST_IFAC "org.dunstproject.cmd0"
#define DUNST_NAME "org.freedesktop.Notifications"
#define PROPERTIES_IFAC "org.freedesktop.DBus.Properties"
GDBusConnection *dbus_conn;
static GDBusNodeInfo *introspection_data = NULL;
static const char *introspection_xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<node name=\""FDN_PATH"\">"
" <interface name=\""FDN_IFAC"\">"
" <method name=\"GetCapabilities\">"
" <arg direction=\"out\" name=\"capabilities\" type=\"as\"/>"
" </method>"
" <method name=\"Notify\">"
" <arg direction=\"in\" name=\"app_name\" type=\"s\"/>"
" <arg direction=\"in\" name=\"replaces_id\" type=\"u\"/>"
" <arg direction=\"in\" name=\"app_icon\" type=\"s\"/>"
" <arg direction=\"in\" name=\"summary\" type=\"s\"/>"
" <arg direction=\"in\" name=\"body\" type=\"s\"/>"
" <arg direction=\"in\" name=\"actions\" type=\"as\"/>"
" <arg direction=\"in\" name=\"hints\" type=\"a{sv}\"/>"
" <arg direction=\"in\" name=\"expire_timeout\" type=\"i\"/>"
" <arg direction=\"out\" name=\"id\" type=\"u\"/>"
" </method>"
" <method name=\"CloseNotification\">"
" <arg direction=\"in\" name=\"id\" type=\"u\"/>"
" </method>"
" <method name=\"GetServerInformation\">"
" <arg direction=\"out\" name=\"name\" type=\"s\"/>"
" <arg direction=\"out\" name=\"vendor\" type=\"s\"/>"
" <arg direction=\"out\" name=\"version\" type=\"s\"/>"
" <arg direction=\"out\" name=\"spec_version\" type=\"s\"/>"
" </method>"
" <signal name=\"NotificationClosed\">"
" <arg name=\"id\" type=\"u\"/>"
" <arg name=\"reason\" type=\"u\"/>"
" </signal>"
" <signal name=\"ActionInvoked\">"
" <arg name=\"id\" type=\"u\"/>"
" <arg name=\"action_key\" type=\"s\"/>"
" </signal>"
" </interface>"
" <interface name=\""DUNST_IFAC"\">"
" <method name=\"ContextMenuCall\" />"
// TODO: add an optional parmater definining the action of notification number X to invoke
" <method name=\"NotificationAction\">"
" <arg name=\"number\" type=\"i\"/>"
" </method>"
" <method name=\"NotificationCloseLast\" />"
" <method name=\"NotificationCloseAll\" />"
" <method name=\"NotificationShow\" />"
" <method name=\"Ping\" />"
" <property name=\"running\" type=\"b\" access=\"readwrite\">"
" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
" </property>"
" </interface>"
"</node>";
static const char *stack_tag_hints[] = {
"synchronous",
"private-synchronous",
"x-canonical-private-synchronous",
"x-dunst-stack-tag"
};
struct dbus_method {
const char *method_name;
void (*method) (GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation);
};
// TODO: call it interface methods
#define DBUS_METHOD(name) static void dbus_cb_##name( \
GDBusConnection *connection, \
const gchar *sender, \
GVariant *parameters, \
GDBusMethodInvocation *invocation)
int cmp_methods(const void *vkey, const void *velem)
{
const char *key = (const char*)vkey;
const struct dbus_method *m = (const struct dbus_method*)velem;
return strcmp(key, m->method_name);
}
DBUS_METHOD(Notify);
DBUS_METHOD(CloseNotification);
DBUS_METHOD(GetCapabilities);
DBUS_METHOD(GetServerInformation);
static struct dbus_method methods_fdn[] = {
{"CloseNotification", dbus_cb_CloseNotification},
{"GetCapabilities", dbus_cb_GetCapabilities},
{"GetServerInformation", dbus_cb_GetServerInformation},
{"Notify", dbus_cb_Notify},
};
void dbus_cb_fdn_methods(GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
struct dbus_method *m = bsearch(method_name,
methods_fdn,
G_N_ELEMENTS(methods_fdn),
sizeof(struct dbus_method),
cmp_methods);
if (m) {
m->method(connection, sender, parameters, invocation);
} else {
LOG_M("Unknown method name: '%s' (sender: '%s').",
method_name,
sender);
}
}
DBUS_METHOD(dunst_ContextMenuCall);
DBUS_METHOD(dunst_NotificationAction);
DBUS_METHOD(dunst_NotificationCloseAll);
DBUS_METHOD(dunst_NotificationCloseLast);
DBUS_METHOD(dunst_NotificationShow);
DBUS_METHOD(dunst_Ping);
static struct dbus_method methods_dunst[] = {
{"ContextMenuCall", dbus_cb_dunst_ContextMenuCall},
{"NotificationAction", dbus_cb_dunst_NotificationAction},
{"NotificationCloseAll", dbus_cb_dunst_NotificationCloseAll},
{"NotificationCloseLast", dbus_cb_dunst_NotificationCloseLast},
{"NotificationShow", dbus_cb_dunst_NotificationShow},
{"Ping", dbus_cb_dunst_Ping},
};
void dbus_cb_dunst_methods(GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
struct dbus_method *m = bsearch(method_name,
methods_dunst,
G_N_ELEMENTS(methods_dunst),
sizeof(struct dbus_method),
cmp_methods);
if (m) {
m->method(connection, sender, parameters, invocation);
} else {
LOG_M("Unknown method name: '%s' (sender: '%s').",
method_name,
sender);
}
}
static void dbus_cb_dunst_ContextMenuCall(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
LOG_D("CMD: Calling context menu");
context_menu();
g_dbus_method_invocation_return_value(invocation, NULL);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
static void dbus_cb_dunst_NotificationAction(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
int notification_nr = 0;
g_variant_get(parameters, "(i)", &notification_nr);
LOG_D("CMD: Calling action for notification %d", notification_nr);
if (notification_nr < 0 || queues_length_waiting() < notification_nr)
return; //FIXME return error
const GList *list = g_list_nth_data(queues_get_displayed(), notification_nr);
if (list && list->data) {
struct notification *n = list->data;
LOG_D("CMD: Calling action for notification %s", n->summary);
notification_do_action(n);
// TODO: do we need to wake up after notification action?
wake_up();
}
g_dbus_method_invocation_return_value(invocation, NULL);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
static void dbus_cb_dunst_NotificationCloseAll(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
LOG_D("CMD: Pushing all to history");
queues_history_push_all();
wake_up();
g_dbus_method_invocation_return_value(invocation, NULL);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
static void dbus_cb_dunst_NotificationCloseLast(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
LOG_D("CMD: Closing last notification");
const GList *list = queues_get_displayed();
if (list && list->data) {
struct notification *n = list->data;
queues_notification_close_id(n->id, REASON_USER);
wake_up();
}
g_dbus_method_invocation_return_value(invocation, NULL);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
static void dbus_cb_dunst_NotificationShow(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
LOG_D("CMD: Showing last notification from history");
queues_history_pop();
wake_up();
g_dbus_method_invocation_return_value(invocation, NULL);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
/* Just a simple Ping command to give the ability to dunstctl to test for the existence of this interface
* Any other way requires parsing the XML of the Introspection or other foo. Just calling the Ping on an old dunst version will fail. */
static void dbus_cb_dunst_Ping(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
g_dbus_method_invocation_return_value(invocation, NULL);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
static void dbus_cb_GetCapabilities(
GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
GVariantBuilder *builder;
GVariant *value;
builder = g_variant_builder_new(G_VARIANT_TYPE("as"));
g_variant_builder_add(builder, "s", "actions");
g_variant_builder_add(builder, "s", "body");
g_variant_builder_add(builder, "s", "body-hyperlinks");
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i)
g_variant_builder_add(builder, "s", stack_tag_hints[i]);
if (settings.markup != MARKUP_NO)
g_variant_builder_add(builder, "s", "body-markup");
value = g_variant_new("(as)", builder);
g_clear_pointer(&builder, g_variant_builder_unref);
g_dbus_method_invocation_return_value(invocation, value);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
static struct notification *dbus_message_to_notification(const gchar *sender, GVariant *parameters)
{
/* Assert that the parameters' type is actually correct. Albeit usually DBus
* already rejects ill typed parameters, it may not be always the case. */
GVariantType *required_type = g_variant_type_new("(susssasa{sv}i)");
if (!g_variant_is_of_type(parameters, required_type)) {
g_variant_type_free(required_type);
return NULL;
}
struct notification *n = notification_create();
n->dbus_client = g_strdup(sender);
n->dbus_valid = true;
GVariant *hints;
gchar **actions;
int timeout;
GVariantIter i;
g_variant_iter_init(&i, parameters);
g_variant_iter_next(&i, "s", &n->appname);
g_variant_iter_next(&i, "u", &n->id);
g_variant_iter_next(&i, "s", &n->iconname);
g_variant_iter_next(&i, "s", &n->summary);
g_variant_iter_next(&i, "s", &n->body);
g_variant_iter_next(&i, "^a&s", &actions);
g_variant_iter_next(&i, "@a{?*}", &hints);
g_variant_iter_next(&i, "i", &timeout);
gsize num = 0;
while (actions[num]) {
if (actions[num+1]) {
g_hash_table_insert(n->actions,
g_strdup(actions[num]),
g_strdup(actions[num+1]));
num+=2;
} else {
LOG_W("Odd length in actions array. Ignoring element: %s", actions[num]);
break;
}
}
GVariant *dict_value;
if ((dict_value = g_variant_lookup_value(hints, "urgency", G_VARIANT_TYPE_BYTE))) {
n->urgency = g_variant_get_byte(dict_value);
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "fgcolor", G_VARIANT_TYPE_STRING))) {
n->colors.fg = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "bgcolor", G_VARIANT_TYPE_STRING))) {
n->colors.bg = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "frcolor", G_VARIANT_TYPE_STRING))) {
n->colors.frame = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "category", G_VARIANT_TYPE_STRING))) {
n->category = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "desktop-entry", G_VARIANT_TYPE_STRING))) {
n->desktop_entry = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "image-path", G_VARIANT_TYPE_STRING))) {
g_free(n->iconname);
n->iconname = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
}
dict_value = g_variant_lookup_value(hints, "image-data", G_VARIANT_TYPE("(iiibiiay)"));
if (!dict_value)
dict_value = g_variant_lookup_value(hints, "image_data", G_VARIANT_TYPE("(iiibiiay)"));
if (!dict_value)
dict_value = g_variant_lookup_value(hints, "icon_data", G_VARIANT_TYPE("(iiibiiay)"));
if (dict_value) {
notification_icon_replace_data(n, dict_value);
g_variant_unref(dict_value);
}
/* Check for transient hints
*
* According to the spec, the transient hint should be boolean.
* But notify-send does not support hints of type 'boolean'.
* So let's check for int and boolean until notify-send is fixed.
*/
if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_BOOLEAN))) {
n->transient = g_variant_get_boolean(dict_value);
g_variant_unref(dict_value);
} else if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_UINT32))) {
n->transient = g_variant_get_uint32(dict_value) > 0;
g_variant_unref(dict_value);
} else if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_INT32))) {
n->transient = g_variant_get_int32(dict_value) > 0;
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "value", G_VARIANT_TYPE_INT32))) {
n->progress = g_variant_get_int32(dict_value);
g_variant_unref(dict_value);
} else if ((dict_value = g_variant_lookup_value(hints, "value", G_VARIANT_TYPE_UINT32))) {
n->progress = g_variant_get_uint32(dict_value);
g_variant_unref(dict_value);
}
/* Check for hints that define the stack_tag
*
* Only accept to first one we find.
*/
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i) {
if ((dict_value = g_variant_lookup_value(hints, stack_tag_hints[i], G_VARIANT_TYPE_STRING))) {
n->stack_tag = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
break;
}
}
if (timeout >= 0)
n->timeout = ((gint64)timeout) * 1000;
g_variant_unref(hints);
g_variant_type_free(required_type);
g_free(actions); // the strv is only a shallow copy
notification_init(n);
return n;
}
static void dbus_cb_Notify(
GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
struct notification *n = dbus_message_to_notification(sender, parameters);
if (!n) {
LOG_W("A notification failed to decode.");
g_dbus_method_invocation_return_dbus_error(
invocation,
FDN_IFAC".Error",
"Cannot decode notification!");
return;
}
int id = queues_notification_insert(n);
GVariant *reply = g_variant_new("(u)", id);
g_dbus_method_invocation_return_value(invocation, reply);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
// The message got discarded
if (id == 0) {
signal_notification_closed(n, REASON_USER);
notification_unref(n);
}
wake_up();
}
static void dbus_cb_CloseNotification(
GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
guint32 id;
g_variant_get(parameters, "(u)", &id);
queues_notification_close_id(id, REASON_SIG);
wake_up();
g_dbus_method_invocation_return_value(invocation, NULL);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
static void dbus_cb_GetServerInformation(
GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
GVariant *answer = g_variant_new("(ssss)", "dunst", "knopwob", VERSION, "1.2");
g_dbus_method_invocation_return_value(invocation, answer);
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
void signal_notification_closed(struct notification *n, enum reason reason)
{
if (!n->dbus_valid) {
LOG_W("Closing notification '%s' not supported. "
"Notification already closed.", n->summary);
return;
}
if (reason < REASON_MIN || REASON_MAX < reason) {
LOG_W("Closing notification with reason '%d' not supported. "
"Closing it with reason '%d'.", reason, REASON_UNDEF);
reason = REASON_UNDEF;
}
if (!dbus_conn) {
LOG_E("Unable to close notification: No DBus connection.");
}
GVariant *body = g_variant_new("(uu)", n->id, reason);
GError *err = NULL;
g_dbus_connection_emit_signal(dbus_conn,
n->dbus_client,
FDN_PATH,
FDN_IFAC,
"NotificationClosed",
body,
&err);
notification_invalidate_actions(n);
n->dbus_valid = false;
if (err) {
LOG_W("Unable to close notification: %s", err->message);
g_error_free(err);
}
}
void signal_action_invoked(const struct notification *n, const char *identifier)
{
if (!n->dbus_valid) {
LOG_W("Invoking action '%s' not supported. "
"Notification already closed.", identifier);
return;
}
GVariant *body = g_variant_new("(us)", n->id, identifier);
GError *err = NULL;
g_dbus_connection_emit_signal(dbus_conn,
n->dbus_client,
FDN_PATH,
FDN_IFAC,
"ActionInvoked",
body,
&err);
if (err) {
LOG_W("Unable to invoke action: %s", err->message);
g_error_free(err);
}
}
//FIXME: Is this necessary or alternative question: Is this implemented correctl?
// This had been an old relict from the manual times, when I haven't used the
// interface vtable of GLib
void dbus_signal_status_changed(struct dunst_status status)
{
// We might have not a working connection yet, so just ignore it.
if (!dbus_conn)
return;
//TODO: I'm pretty sure this is the right format string, but I don't know how to verify it
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE("(sa{sv}as)"));
g_variant_builder_add(&builder, "s", DUNST_IFAC);
g_variant_builder_open(&builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add(&builder, "{sv}", "running", g_variant_new_boolean(status.running));
g_variant_builder_close(&builder);
g_variant_builder_open(&builder, G_VARIANT_TYPE ("as"));
g_variant_builder_add(&builder, "s", "unrelated");
g_variant_builder_close(&builder);
GError *err = NULL;
g_dbus_connection_emit_signal(dbus_conn,
NULL,
DUNST_PATH,
PROPERTIES_IFAC,
"PropertiesChanged",
g_variant_builder_end(&builder),
&err);
if (err) {
LOG_W("Unable send signal 'PropertiesChanged': %s", err->message);
g_error_free(err);
}
}
GVariant *dbus_cb_dunst_Properties_Get(GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
struct dunst_status status = dunst_status_get();
if (STR_EQ(property_name, "running"))
return g_variant_new_boolean(status.running);
else
//TODO: is NULL as return value allowed?
return NULL;
}
gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error,
gpointer user_data)
{
if (STR_EQ(property_name, "running")) {
dunst_status(S_RUNNING, g_variant_get_boolean(value));
return true;
}
//FIXME: don't we have to return true on successful setting, but return false, if e.g. the parameter name is wrong?
//return true;
// so like this?
return false;
}
static const GDBusInterfaceVTable interface_vtable_fdn = {
dbus_cb_fdn_methods
};
static const GDBusInterfaceVTable interface_vtable_dunst = {
dbus_cb_dunst_methods,
dbus_cb_dunst_Properties_Get,
dbus_cb_dunst_Properties_Set,
};
static void dbus_cb_bus_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
// TODO: deduplicate the code
GError *err = NULL;
if(!g_dbus_connection_register_object(
connection,
FDN_PATH,
introspection_data->interfaces[0],
&interface_vtable_fdn,
NULL,
NULL,
&err)) {
DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[0]->name, err->message);
}
if(!g_dbus_connection_register_object(
connection,
FDN_PATH,
introspection_data->interfaces[1],
&interface_vtable_dunst,
NULL,
NULL,
&err)) {
DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[1]->name, err->message);
}
}
static void dbus_cb_name_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
// If we're not able to get org.fd.N bus, we've still got a problem
if (STR_EQ(name, FDN_NAME))
dbus_conn = connection;
}
/**
* Get the PID of the current process, which acquired FDN DBus Name.
*
* If name or vendor specified, the name and vendor
* will get additionally get via the FDN GetServerInformation method
*
* @param connection The DBus connection
* @param pid The place to report the PID to
* @param name The place to report the name to, if not required set to NULL
* @param vendor The place to report the vendor to, if not required set to NULL
*
* @retval true: on success
* @retval false: Any error happened
*/
static bool dbus_get_fdn_daemon_info(GDBusConnection *connection,
guint *pid,
char **name,
char **vendor)
{
ASSERT_OR_RET(pid, false);
ASSERT_OR_RET(connection, false);
char *owner = NULL;
GError *error = NULL;
GDBusProxy *proxy_fdn;
GDBusProxy *proxy_dbus;
proxy_fdn = g_dbus_proxy_new_sync(
connection,
/* do not trigger a start of the notification daemon */
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
NULL, /* info */
FDN_NAME,
FDN_PATH,
FDN_IFAC,
NULL, /* cancelable */
&error);
if (error) {
g_error_free(error);
return false;
}
GVariant *daemoninfo = NULL;
if (name || vendor) {
daemoninfo = g_dbus_proxy_call_sync(
proxy_fdn,
FDN_IFAC ".GetServerInformation",
NULL,
G_DBUS_CALL_FLAGS_NONE,
/* It's not worth to wait for the info
* longer than half a second when dying */
500,
NULL, /* cancelable */
&error);
}
if (error) {
/* Ignore the error, we may still be able to retrieve the PID */
g_clear_pointer(&error, g_error_free);
} else {
g_variant_get(daemoninfo, "(ssss)", name, vendor, NULL, NULL);
}
owner = g_dbus_proxy_get_name_owner(proxy_fdn);
proxy_dbus = g_dbus_proxy_new_sync(
connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL, /* info */
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
NULL, /* cancelable */
&error);
if (error) {
g_error_free(error);
return false;
}
GVariant *pidinfo = g_dbus_proxy_call_sync(
proxy_dbus,
"org.freedesktop.DBus.GetConnectionUnixProcessID",
g_variant_new("(s)", owner),
G_DBUS_CALL_FLAGS_NONE,
/* It's not worth to wait for the PID
* longer than half a second when dying */
500,
NULL,
&error);
if (error) {
g_error_free(error);
return false;
}
g_object_unref(proxy_fdn);
g_object_unref(proxy_dbus);
g_free(owner);
if (daemoninfo)
g_variant_unref(daemoninfo);
if (pidinfo) {
g_variant_get(pidinfo, "(u)", pid);
g_variant_unref(pidinfo);
return true;
} else {
return false;
}
}
static void dbus_cb_name_lost(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
if (connection) {
char *name;
unsigned int pid;
if (dbus_get_fdn_daemon_info(connection, &pid, &name, NULL)) {
DIE("Cannot acquire '"FDN_NAME"': "
"Name is acquired by '%s' with PID '%d'.", name, pid);
} else {
DIE("Cannot acquire '"FDN_NAME"'.");
}
} else {
DIE("Cannot connect to DBus.");
}
exit(1);
}
int dbus_init(void)
{
guint owner_id;
introspection_data = g_dbus_node_info_new_for_xml(introspection_xml,
NULL);
owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
FDN_NAME,
G_BUS_NAME_OWNER_FLAGS_NONE,
dbus_cb_bus_acquired,
dbus_cb_name_acquired,
dbus_cb_name_lost,
NULL,
NULL);
return owner_id;
}
void dbus_teardown(int owner_id)
{
g_clear_pointer(&introspection_data, g_dbus_node_info_unref);
g_bus_unown_name(owner_id);
}
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */