Initial GDBus code-drop from GDBus-standalone repo

Things compile and the test-suite passes. Still need to hook up
gio.symbols and docs. There are still a bunch of TODOs left in the
sources that needs to be addressed.

Signed-off-by: David Zeuthen <davidz@redhat.com>
This commit is contained in:
David Zeuthen 2010-05-06 14:13:59 -04:00
parent 81e98c399e
commit d0a14469d0
74 changed files with 35927 additions and 11 deletions

View File

@ -3496,6 +3496,16 @@ if test x$glib_win32_static_compilation = xyes; then
fi
])
# Check for libdbus1 - Optional - is only used in the GDBus test cases
#
PKG_CHECK_MODULES(DBUS1,
dbus-1,
[AC_DEFINE(HAVE_DBUS1, 1, [Define if dbus-1 is available]) have_dbus1=yes],
have_dbus1=no)
AC_SUBST(DBUS1_CFLAGS)
AC_SUBST(DBUS1_LIBS)
AM_CONDITIONAL(HAVE_DBUS1, [test "x$have_dbus1" = "xyes"])
AC_CONFIG_FILES([
glib-2.0.pc
glib-2.0-uninstalled.pc

View File

@ -79,6 +79,48 @@ gio-marshal.c: gio-marshal.h gio-marshal.list
$(glib_genmarshal) --prefix=_gio_marshal $(srcdir)/gio-marshal.list --body --internal) > $@.tmp && \
mv $@.tmp $@
gdbus_headers = \
gdbusauthobserver.h \
gcredentials.h \
gunixcredentialsmessage.h \
gdbusutils.h \
gdbuserror.h \
gdbusaddress.h \
gdbusconnection.h \
gdbusmessage.h \
gdbusnameowning.h \
gdbusnamewatching.h \
gdbusproxywatching.h \
gdbusproxy.h \
gdbusintrospection.h \
gdbusmethodinvocation.h \
gdbusserver.h \
$(NULL)
gdbus_sources = \
gdbusutils.h gdbusutils.c \
gcredentials.h gcredentials.c \
gunixcredentialsmessage.h gunixcredentialsmessage.c \
gdbusaddress.h gdbusaddress.c \
gdbusauthobserver.h gdbusauthobserver.c \
gdbusauth.h gdbusauth.c \
gdbusauthmechanism.h gdbusauthmechanism.c \
gdbusauthmechanismanon.h gdbusauthmechanismanon.c \
gdbusauthmechanismexternal.h gdbusauthmechanismexternal.c \
gdbusauthmechanismsha1.h gdbusauthmechanismsha1.c \
gdbuserror.h gdbuserror.c \
gdbusconnection.h gdbusconnection.c \
gdbusmessage.h gdbusmessage.c \
gdbusnameowning.h gdbusnameowning.c \
gdbusnamewatching.h gdbusnamewatching.c \
gdbusproxywatching.h gdbusproxywatching.c \
gdbusproxy.h gdbusproxy.c \
gdbusprivate.h gdbusprivate.c \
gdbusintrospection.h gdbusintrospection.c \
gdbusmethodinvocation.h gdbusmethodinvocation.c \
gdbusserver.h gdbusserver.c \
$(NULL)
settings_headers = \
gsettingsbackend.h \
gsettings.h
@ -327,6 +369,7 @@ libgio_2_0_la_SOURCES = \
$(unix_sources) \
$(win32_sources) \
$(settings_sources) \
$(gdbus_sources) \
$(local_sources) \
$(marshal_sources) \
$(NULL)
@ -454,6 +497,7 @@ gio_headers = \
gzlibcompressor.h \
gzlibdecompressor.h \
$(settings_headers) \
$(gdbus_headers) \
$(NULL)
gioincludedir=$(includedir)/glib-2.0/gio/
@ -527,10 +571,22 @@ gsettings_LDADD = \
libgio-2.0.la
gsettings_SOURCES = gsettings-tool.c
schemadir = $(datadir)/glib-2.0/schemas
dist_schema_DATA = gschema.dtd
# ------------------------------------------------------------------------
# gdbus(1) tool
bin_PROGRAMS += gdbus
gdbus_SOURCES = gdbus-tool.c
gdbus_LDADD = libgio-2.0.la
completiondir = $(sysconfdir)/bash_completion.d
completion_SCRIPTS = gdbus-bash-completion.sh
EXTRA_DIST += $(completion_SCRIPTS)
# ------------------------------------------------------------------------
dist-hook: $(BUILT_EXTRA_DIST) ../build/win32/vs9/gio.vcproj
files='$(BUILT_EXTRA_DIST)'; \
for f in $$files; do \

427
gio/gcredentials.c Normal file
View File

@ -0,0 +1,427 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <glib/gi18n.h>
#include <gobject/gvaluecollector.h>
#include "gcredentials.h"
#include "gioerror.h"
#ifdef G_OS_UNIX
#include <sys/types.h>
#include <unistd.h>
#endif
/**
* SECTION:gcredentials
* @short_description: Credentials
* @include: gdbus/gdbus.h
*
* The #GCredentials type is used for storing information that can be
* used for identifying, authenticating and authorizing processes.
*
* Most UNIX and UNIX-like operating systems support a secure exchange
* of credentials over a Unix Domain Socket, see
* #GUnixCredentialsMessage, g_unix_connection_send_credentials() and
* g_unix_connection_receive_credentials() for details.
*/
struct _GCredentialsPrivate
{
gint64 unix_user;
gint64 unix_group;
gint64 unix_process;
gchar *windows_user;
};
G_DEFINE_TYPE (GCredentials, g_credentials, G_TYPE_OBJECT);
static void
g_credentials_finalize (GObject *object)
{
GCredentials *credentials = G_CREDENTIALS (object);
g_free (credentials->priv->windows_user);
if (G_OBJECT_CLASS (g_credentials_parent_class)->finalize != NULL)
G_OBJECT_CLASS (g_credentials_parent_class)->finalize (object);
}
static void
g_credentials_class_init (GCredentialsClass *klass)
{
GObjectClass *gobject_class;
g_type_class_add_private (klass, sizeof (GCredentialsPrivate));
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_credentials_finalize;
}
static void
g_credentials_init (GCredentials *credentials)
{
credentials->priv = G_TYPE_INSTANCE_GET_PRIVATE (credentials, G_TYPE_CREDENTIALS, GCredentialsPrivate);
credentials->priv->unix_user = -1;
credentials->priv->unix_group = -1;
credentials->priv->unix_process = -1;
credentials->priv->windows_user = NULL;
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_new:
*
* Creates a new empty credentials object.
*
* Returns: A #GCredentials. Free with g_object_unref().
*/
GCredentials *
g_credentials_new (void)
{
return g_object_new (G_TYPE_CREDENTIALS, NULL);
}
/* ---------------------------------------------------------------------------------------------------- */
#ifdef G_OS_UNIX
static GCredentials *
g_credentials_new_for_unix_process (void)
{
GCredentials *credentials;
credentials = g_credentials_new ();
credentials->priv->unix_user = getuid ();
credentials->priv->unix_group = getgid ();
credentials->priv->unix_process = getpid ();
return credentials;
}
#endif
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_new_for_process:
*
* Gets the credentials for the current process. Note that the exact
* set of credentials in the returned object vary according to
* platform.
*
* Returns: A #GCredentials. Free with g_object_unref().
*/
GCredentials *
g_credentials_new_for_process (void)
{
#ifdef G_OS_UNIX
return g_credentials_new_for_unix_process ();
#elif G_OS_WIN32
return g_credentials_new_for_win32_process ();
#else
#warning Please implement g_credentials_new_for_process() for your OS. For now g_credentials_new_for_process() will return empty credentials.
return g_credentials_new ();
#endif
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_new_for_string:
* @str: A string returned from g_credentials_to_string().
* @error: Return location for error.
*
* Constructs a #GCredentials instance from @str.
*
* Returns: A #GCredentials or %NULL if @error is set. The return
* object must be freed with g_object_unref().
*/
GCredentials *
g_credentials_new_for_string (const gchar *str,
GError **error)
{
GCredentials *credentials;
gchar **tokens;
guint n;
g_return_val_if_fail (str != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
tokens = NULL;
credentials = g_credentials_new ();
if (!g_str_has_prefix (str, "GCredentials:"))
goto fail;
tokens = g_strsplit (str + sizeof "GCredentials:" - 1, ",", 0);
for (n = 0; tokens[n] != NULL; n++)
{
const gchar *token = tokens[n];
if (g_str_has_prefix (token, "unix-user:"))
g_credentials_set_unix_user (credentials, atoi (token + sizeof ("unix-user:") - 1));
else if (g_str_has_prefix (token, "unix-group:"))
g_credentials_set_unix_group (credentials, atoi (token + sizeof ("unix-group:") - 1));
else if (g_str_has_prefix (token, "unix-process:"))
g_credentials_set_unix_process (credentials, atoi (token + sizeof ("unix-process:") - 1));
else if (g_str_has_prefix (token, "windows-user:"))
g_credentials_set_windows_user (credentials, token + sizeof ("windows-user:"));
else
goto fail;
}
g_strfreev (tokens);
return credentials;
fail:
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
_("The string `%s' is not a valid credentials string"),
str);
g_object_unref (credentials);
g_strfreev (tokens);
return NULL;
}
/**
* g_credentials_to_string:
* @credentials: A #GCredentials object.
*
* Serializes @credentials to a string that can be used with
* g_credentials_new_for_string().
*
* Returns: A string that should be freed with g_free().
*/
gchar *
g_credentials_to_string (GCredentials *credentials)
{
GString *ret;
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
ret = g_string_new ("GCredentials:");
if (credentials->priv->unix_user != -1)
g_string_append_printf (ret, "unix-user=%" G_GINT64_FORMAT ",", credentials->priv->unix_user);
if (credentials->priv->unix_group != -1)
g_string_append_printf (ret, "unix-group=%" G_GINT64_FORMAT ",", credentials->priv->unix_group);
if (credentials->priv->unix_process != -1)
g_string_append_printf (ret, "unix-process=%" G_GINT64_FORMAT ",", credentials->priv->unix_process);
if (credentials->priv->windows_user != NULL)
g_string_append_printf (ret, "windows-user=%s,", credentials->priv->windows_user);
if (ret->str[ret->len - 1] == ',')
ret->str[ret->len - 1] = '\0';
return g_string_free (ret, FALSE);
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_has_unix_user:
* @credentials: A #GCredentials.
*
* Checks if @credentials has a UNIX user credential.
*
* Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise.
*/
gboolean
g_credentials_has_unix_user (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
return credentials->priv->unix_user != -1;
}
/**
* g_credentials_get_unix_user:
* @credentials: A #GCredentials.
*
* Gets the UNIX user identifier from @credentials.
*
* Returns: The identifier or -1 if unset.
*/
gint64
g_credentials_get_unix_user (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
return credentials->priv->unix_user;
}
/**
* g_credentials_set_unix_user:
* @credentials: A #GCredentials.
* @user_id: A UNIX user identifier (typically type #uid_t) or -1 to unset it.
*
* Sets the UNIX user identifier.
*/
void
g_credentials_set_unix_user (GCredentials *credentials,
gint64 user_id)
{
g_return_if_fail (G_IS_CREDENTIALS (credentials));
credentials->priv->unix_user = user_id;
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_has_unix_group:
* @credentials: A #GCredentials.
*
* Checks if @credentials has a UNIX group credential.
*
* Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise.
*/
gboolean
g_credentials_has_unix_group (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
return credentials->priv->unix_group != -1;
}
/**
* g_credentials_get_unix_group:
* @credentials: A #GCredentials.
*
* Gets the UNIX group identifier from @credentials.
*
* Returns: The identifier or -1 if unset.
*/
gint64
g_credentials_get_unix_group (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
return credentials->priv->unix_group;
}
/**
* g_credentials_set_unix_group:
* @credentials: A #GCredentials.
* @group_id: A UNIX group identifier (typically type #gid_t) or -1 to unset.
*
* Sets the UNIX group identifier.
*/
void
g_credentials_set_unix_group (GCredentials *credentials,
gint64 group_id)
{
g_return_if_fail (G_IS_CREDENTIALS (credentials));
credentials->priv->unix_group = group_id;
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_has_unix_process:
* @credentials: A #GCredentials.
*
* Checks if @credentials has a UNIX process credential.
*
* Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise.
*/
gboolean
g_credentials_has_unix_process (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
return credentials->priv->unix_process != -1;
}
/**
* g_credentials_get_unix_process:
* @credentials: A #GCredentials.
*
* Gets the UNIX process identifier from @credentials.
*
* Returns: The identifier or -1 if unset.
*/
gint64
g_credentials_get_unix_process (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
return credentials->priv->unix_process;
}
/**
* g_credentials_set_unix_process:
* @credentials: A #GCredentials.
* @process_id: A UNIX process identifier (typically type #pid_t/#GPid) or -1 to unset.
*
* Sets the UNIX process identifier.
*/
void
g_credentials_set_unix_process (GCredentials *credentials,
gint64 process_id)
{
g_return_if_fail (G_IS_CREDENTIALS (credentials));
credentials->priv->unix_process = process_id;
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_has_windows_user:
* @credentials: A #GCredentials.
*
* Checks if @credentials has a Windows user SID (Security Identifier).
*
* Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise.
*/
gboolean
g_credentials_has_windows_user (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
return credentials->priv->windows_user != NULL;
}
/**
* g_credentials_get_windows_user:
* @credentials: A #GCredentials.
*
* Gets the Windows User SID from @credentials.
*
* Returns: A string or %NULL if unset. Do not free, the string is owned by @credentials.
*/
const gchar *
g_credentials_get_windows_user (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
return credentials->priv->windows_user;
}
/**
* g_credentials_set_windows_user:
* @credentials: A #GCredentials.
* @user_sid: The Windows User SID or %NULL to unset.
*
* Sets the Windows User SID.
*/
void
g_credentials_set_windows_user (GCredentials *credentials,
const gchar *user_sid)
{
g_return_if_fail (G_IS_CREDENTIALS (credentials));
g_free (credentials->priv->windows_user);
credentials->priv->windows_user = g_strdup (user_sid);
}
/* ---------------------------------------------------------------------------------------------------- */

104
gio/gcredentials.h Normal file
View File

@ -0,0 +1,104 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_CREDENTIALS_H__
#define __G_CREDENTIALS_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_CREDENTIALS (g_credentials_get_type ())
#define G_CREDENTIALS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CREDENTIALS, GCredentials))
#define G_CREDENTIALS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CREDENTIALS, GCredentialsClass))
#define G_CREDENTIALS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CREDENTIALS, GCredentialsClass))
#define G_IS_CREDENTIALS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CREDENTIALS))
#define G_IS_CREDENTIALS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CREDENTIALS))
typedef struct _GCredentialsClass GCredentialsClass;
typedef struct _GCredentialsPrivate GCredentialsPrivate;
/**
* GCredentials:
*
* The #GCredentials structure contains only private data and
* should only be accessed using the provided API.
*/
struct _GCredentials
{
/*< private >*/
GObject parent_instance;
GCredentialsPrivate *priv;
};
/**
* GCredentialsClass:
*
* Class structure for #GCredentials.
*/
struct _GCredentialsClass
{
/*< private >*/
GObjectClass parent_class;
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
};
GType g_credentials_get_type (void) G_GNUC_CONST;
GCredentials *g_credentials_new (void);
GCredentials *g_credentials_new_for_process (void);
GCredentials *g_credentials_new_for_string (const gchar *str,
GError **error);
gchar *g_credentials_to_string (GCredentials *credentials);
gboolean g_credentials_has_unix_user (GCredentials *credentials);
gint64 g_credentials_get_unix_user (GCredentials *credentials);
void g_credentials_set_unix_user (GCredentials *credentials,
gint64 user_id);
gboolean g_credentials_has_unix_group (GCredentials *credentials);
gint64 g_credentials_get_unix_group (GCredentials *credentials);
void g_credentials_set_unix_group (GCredentials *credentials,
gint64 group_id);
gboolean g_credentials_has_unix_process (GCredentials *credentials);
gint64 g_credentials_get_unix_process (GCredentials *credentials);
void g_credentials_set_unix_process (GCredentials *credentials,
gint64 process_id);
gboolean g_credentials_has_windows_user (GCredentials *credentials);
const gchar *g_credentials_get_windows_user (GCredentials *credentials);
void g_credentials_set_windows_user (GCredentials *credentials,
const gchar *user_sid);
G_END_DECLS
#endif /* __G_DBUS_PROXY_H__ */

View File

@ -0,0 +1,33 @@
# Check for bash
[ -z "$BASH_VERSION" ] && return
####################################################################################################
__gdbus() {
local IFS=$'\n'
local cur=`_get_cword :`
local suggestions=$(gdbus complete "${COMP_LINE}" ${COMP_POINT})
COMPREPLY=($(compgen -W "$suggestions" -- "$cur"))
# Remove colon-word prefix from COMPREPLY items
case "$cur" in
*:*)
case "$COMP_WORDBREAKS" in
*:*)
local colon_word=${cur%${cur##*:}}
local i=${#COMPREPLY[*]}
while [ $((--i)) -ge 0 ]; do
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
done
;;
esac
;;
esac
}
####################################################################################################
complete -o nospace -F __gdbus gdbus

1491
gio/gdbus-tool.c Normal file

File diff suppressed because it is too large Load Diff

1004
gio/gdbusaddress.c Normal file

File diff suppressed because it is too large Load Diff

54
gio/gdbusaddress.h Normal file
View File

@ -0,0 +1,54 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_ADDRESS_H__
#define __G_DBUS_ADDRESS_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
gboolean g_dbus_is_address (const gchar *string);
gboolean g_dbus_is_supported_address (const gchar *string,
GError **error);
void g_dbus_address_get_stream (const gchar *address,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GIOStream *g_dbus_address_get_stream_finish (GAsyncResult *res,
gchar **out_guid,
GError **error);
GIOStream *g_dbus_address_get_stream_sync (const gchar *address,
gchar **out_guid,
GCancellable *cancellable,
GError **error);
gchar *g_dbus_address_get_for_bus_sync (GBusType bus_type,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_DBUS_ADDRESS_H__ */

1538
gio/gdbusauth.c Normal file

File diff suppressed because it is too large Load Diff

86
gio/gdbusauth.h Normal file
View File

@ -0,0 +1,86 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#if !defined (GIO_COMPILATION)
#error "gdbusauth.h is a private header file."
#endif
#ifndef __G_DBUS_AUTH_H__
#define __G_DBUS_AUTH_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH (_g_dbus_auth_get_type ())
#define G_DBUS_AUTH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH, GDBusAuth))
#define G_DBUS_AUTH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH, GDBusAuthClass))
#define G_DBUS_AUTH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH, GDBusAuthClass))
#define G_IS_DBUS_AUTH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH))
#define G_IS_DBUS_AUTH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH))
typedef struct _GDBusAuth GDBusAuth;
typedef struct _GDBusAuthClass GDBusAuthClass;
typedef struct _GDBusAuthPrivate GDBusAuthPrivate;
struct _GDBusAuthClass
{
/*< private >*/
GObjectClass parent_class;
};
struct _GDBusAuth
{
GObject parent_instance;
GDBusAuthPrivate *priv;
};
GType _g_dbus_auth_get_type (void) G_GNUC_CONST;
GDBusAuth *_g_dbus_auth_new (GIOStream *stream);
/* TODO: need a way to set allowed authentication mechanisms */
/* TODO: need a way to convey credentials etc. */
/* TODO: need a way to convey negotiated features (e.g. returning flags from e.g. GDBusConnectionFeatures) */
/* TODO: need to expose encode()/decode() from the AuthMechanism (and whether it is needed at all) */
gboolean _g_dbus_auth_run_server (GDBusAuth *auth,
GDBusAuthObserver *observer,
const gchar *guid,
gboolean allow_anonymous,
GDBusCapabilityFlags offered_capabilities,
GDBusCapabilityFlags *out_negotiated_capabilities,
GCredentials **out_received_credentials,
GCancellable *cancellable,
GError **error);
gchar *_g_dbus_auth_run_client (GDBusAuth *auth,
GDBusCapabilityFlags offered_capabilities,
GDBusCapabilityFlags *out_negotiated_capabilities,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_DBUS_AUTH_H__ */

342
gio/gdbusauthmechanism.c Normal file
View File

@ -0,0 +1,342 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <glib/gi18n.h>
#include "gdbusauthmechanism.h"
#include "gcredentials.h"
#include "gdbuserror.h"
#include "gioenumtypes.h"
#include "giostream.h"
/* ---------------------------------------------------------------------------------------------------- */
struct _GDBusAuthMechanismPrivate
{
GIOStream *stream;
GCredentials *credentials;
};
enum
{
PROP_0,
PROP_STREAM,
PROP_CREDENTIALS
};
G_DEFINE_ABSTRACT_TYPE (GDBusAuthMechanism, _g_dbus_auth_mechanism, G_TYPE_OBJECT);
/* ---------------------------------------------------------------------------------------------------- */
static void
_g_dbus_auth_mechanism_finalize (GObject *object)
{
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
if (mechanism->priv->stream != NULL)
g_object_unref (mechanism->priv->stream);
if (mechanism->priv->credentials != NULL)
g_object_unref (mechanism->priv->credentials);
if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize != NULL)
G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize (object);
}
static void
_g_dbus_auth_mechanism_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
switch (prop_id)
{
case PROP_STREAM:
g_value_set_object (value, mechanism->priv->stream);
break;
case PROP_CREDENTIALS:
g_value_set_object (value, mechanism->priv->credentials);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
_g_dbus_auth_mechanism_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
switch (prop_id)
{
case PROP_STREAM:
mechanism->priv->stream = g_value_dup_object (value);
break;
case PROP_CREDENTIALS:
mechanism->priv->credentials = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
_g_dbus_auth_mechanism_class_init (GDBusAuthMechanismClass *klass)
{
GObjectClass *gobject_class;
g_type_class_add_private (klass, sizeof (GDBusAuthMechanismPrivate));
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = _g_dbus_auth_mechanism_get_property;
gobject_class->set_property = _g_dbus_auth_mechanism_set_property;
gobject_class->finalize = _g_dbus_auth_mechanism_finalize;
g_object_class_install_property (gobject_class,
PROP_STREAM,
g_param_spec_object ("stream",
_("IO Stream"),
_("The underlying GIOStream used for I/O"),
G_TYPE_IO_STREAM,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusAuthMechanism:credentials:
*
* If authenticating as a server, this property contains the
* received credentials, if any.
*
* If authenticating as a client, the property contains the
* credentials that were sent, if any.
*/
g_object_class_install_property (gobject_class,
PROP_CREDENTIALS,
g_param_spec_object ("credentials",
_("Credentials"),
_("The credentials of the remote peer"),
G_TYPE_CREDENTIALS,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
}
static void
_g_dbus_auth_mechanism_init (GDBusAuthMechanism *mechanism)
{
/* not used for now */
mechanism->priv = G_TYPE_INSTANCE_GET_PRIVATE (mechanism,
G_TYPE_DBUS_AUTH_MECHANISM,
GDBusAuthMechanismPrivate);
}
/* ---------------------------------------------------------------------------------------------------- */
GIOStream *
_g_dbus_auth_mechanism_get_stream (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return mechanism->priv->stream;
}
GCredentials *
_g_dbus_auth_mechanism_get_credentials (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return mechanism->priv->credentials;
}
/* ---------------------------------------------------------------------------------------------------- */
const gchar *
_g_dbus_auth_mechanism_get_name (GType mechanism_type)
{
const gchar *name;
GDBusAuthMechanismClass *klass;
g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), NULL);
klass = g_type_class_ref (mechanism_type);
g_assert (klass != NULL);
name = klass->get_name ();
//g_type_class_unref (klass);
return name;
}
gint
_g_dbus_auth_mechanism_get_priority (GType mechanism_type)
{
gint priority;
GDBusAuthMechanismClass *klass;
g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), 0);
klass = g_type_class_ref (mechanism_type);
g_assert (klass != NULL);
priority = klass->get_priority ();
//g_type_class_unref (klass);
return priority;
}
/* ---------------------------------------------------------------------------------------------------- */
gboolean
_g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), FALSE);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->is_supported (mechanism);
}
gchar *
_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->encode_data (mechanism, data, data_len, out_data_len);
}
gchar *
_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->decode_data (mechanism, data, data_len, out_data_len);
}
/* ---------------------------------------------------------------------------------------------------- */
GDBusAuthMechanismState
_g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_state (mechanism);
}
void
_g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_initiate (mechanism, initial_response, initial_response_len);
}
void
_g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_receive (mechanism, data, data_len);
}
gchar *
_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_send (mechanism, out_data_len);
}
gchar *
_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_reject_reason (mechanism);
}
void
_g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_shutdown (mechanism);
}
/* ---------------------------------------------------------------------------------------------------- */
GDBusAuthMechanismState
_g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_state (mechanism);
}
gchar *
_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_initiate (mechanism,
out_initial_response_len);
}
void
_g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_receive (mechanism, data, data_len);
}
gchar *
_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_send (mechanism, out_data_len);
}
void
_g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_shutdown (mechanism);
}
/* ---------------------------------------------------------------------------------------------------- */

174
gio/gdbusauthmechanism.h Normal file
View File

@ -0,0 +1,174 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#if !defined (GIO_COMPILATION)
#error "gdbusauthmechanism.h is a private header file."
#endif
#ifndef __G_DBUS_AUTH_MECHANISM_H__
#define __G_DBUS_AUTH_MECHANISM_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_MECHANISM (_g_dbus_auth_mechanism_get_type ())
#define G_DBUS_AUTH_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanism))
#define G_DBUS_AUTH_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanismClass))
#define G_DBUS_AUTH_MECHANISM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanismClass))
#define G_IS_DBUS_AUTH_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM))
#define G_IS_DBUS_AUTH_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM))
typedef struct _GDBusAuthMechanism GDBusAuthMechanism;
typedef struct _GDBusAuthMechanismClass GDBusAuthMechanismClass;
typedef struct _GDBusAuthMechanismPrivate GDBusAuthMechanismPrivate;
typedef enum {
G_DBUS_AUTH_MECHANISM_STATE_INVALID,
G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA,
G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND,
G_DBUS_AUTH_MECHANISM_STATE_REJECTED,
G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED,
} GDBusAuthMechanismState;
struct _GDBusAuthMechanismClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
/* VTable */
/* TODO: server_initiate and client_initiate probably needs to have a
* GCredentials parameter...
*/
gint (*get_priority) (void);
const gchar *(*get_name) (void);
/* functions shared by server/client */
gboolean (*is_supported) (GDBusAuthMechanism *mechanism);
gchar *(*encode_data) (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
gchar *(*decode_data) (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
/* functions for server-side authentication */
GDBusAuthMechanismState (*server_get_state) (GDBusAuthMechanism *mechanism);
void (*server_initiate) (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len);
void (*server_data_receive) (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
gchar *(*server_data_send) (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
gchar *(*server_get_reject_reason) (GDBusAuthMechanism *mechanism);
void (*server_shutdown) (GDBusAuthMechanism *mechanism);
/* functions for client-side authentication */
GDBusAuthMechanismState (*client_get_state) (GDBusAuthMechanism *mechanism);
gchar *(*client_initiate) (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len);
void (*client_data_receive) (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
gchar *(*client_data_send) (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
void (*client_shutdown) (GDBusAuthMechanism *mechanism);
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
void (*_g_reserved9) (void);
void (*_g_reserved10) (void);
void (*_g_reserved11) (void);
void (*_g_reserved12) (void);
void (*_g_reserved13) (void);
void (*_g_reserved14) (void);
void (*_g_reserved15) (void);
void (*_g_reserved16) (void);
};
struct _GDBusAuthMechanism
{
GObject parent_instance;
GDBusAuthMechanismPrivate *priv;
};
GType _g_dbus_auth_mechanism_get_type (void) G_GNUC_CONST;
gint _g_dbus_auth_mechanism_get_priority (GType mechanism_type);
const gchar *_g_dbus_auth_mechanism_get_name (GType mechanism_type);
GIOStream *_g_dbus_auth_mechanism_get_stream (GDBusAuthMechanism *mechanism);
GCredentials *_g_dbus_auth_mechanism_get_credentials (GDBusAuthMechanism *mechanism);
gboolean _g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism);
gchar *_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
gchar *_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
GDBusAuthMechanismState _g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism);
void _g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len);
void _g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
gchar *_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
gchar *_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
void _g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
GDBusAuthMechanismState _g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism);
gchar *_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len);
void _g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
gchar *_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
void _g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
G_END_DECLS
#endif /* __G_DBUS_AUTH_MECHANISM_H__ */

View File

@ -0,0 +1,327 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <glib/gi18n.h>
#include "gdbusauthmechanismanon.h"
#include "gdbuserror.h"
#include "gioenumtypes.h"
struct _GDBusAuthMechanismAnonPrivate
{
gboolean is_client;
gboolean is_server;
GDBusAuthMechanismState state;
};
static gint mechanism_get_priority (void);
static const gchar *mechanism_get_name (void);
static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len);
static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len);
static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
/* ---------------------------------------------------------------------------------------------------- */
G_DEFINE_TYPE (GDBusAuthMechanismAnon, _g_dbus_auth_mechanism_anon, G_TYPE_DBUS_AUTH_MECHANISM);
/* ---------------------------------------------------------------------------------------------------- */
static void
_g_dbus_auth_mechanism_anon_finalize (GObject *object)
{
//GDBusAuthMechanismAnon *mechanism = G_DBUS_AUTH_MECHANISM_ANON (object);
if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_anon_parent_class)->finalize != NULL)
G_OBJECT_CLASS (_g_dbus_auth_mechanism_anon_parent_class)->finalize (object);
}
static void
_g_dbus_auth_mechanism_anon_class_init (GDBusAuthMechanismAnonClass *klass)
{
GObjectClass *gobject_class;
GDBusAuthMechanismClass *mechanism_class;
g_type_class_add_private (klass, sizeof (GDBusAuthMechanismAnonPrivate));
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = _g_dbus_auth_mechanism_anon_finalize;
mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
mechanism_class->get_priority = mechanism_get_priority;
mechanism_class->get_name = mechanism_get_name;
mechanism_class->is_supported = mechanism_is_supported;
mechanism_class->encode_data = mechanism_encode_data;
mechanism_class->decode_data = mechanism_decode_data;
mechanism_class->server_get_state = mechanism_server_get_state;
mechanism_class->server_initiate = mechanism_server_initiate;
mechanism_class->server_data_receive = mechanism_server_data_receive;
mechanism_class->server_data_send = mechanism_server_data_send;
mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
mechanism_class->server_shutdown = mechanism_server_shutdown;
mechanism_class->client_get_state = mechanism_client_get_state;
mechanism_class->client_initiate = mechanism_client_initiate;
mechanism_class->client_data_receive = mechanism_client_data_receive;
mechanism_class->client_data_send = mechanism_client_data_send;
mechanism_class->client_shutdown = mechanism_client_shutdown;
}
static void
_g_dbus_auth_mechanism_anon_init (GDBusAuthMechanismAnon *mechanism)
{
mechanism->priv = G_TYPE_INSTANCE_GET_PRIVATE (mechanism,
G_TYPE_DBUS_AUTH_MECHANISM_ANON,
GDBusAuthMechanismAnonPrivate);
}
/* ---------------------------------------------------------------------------------------------------- */
static gint
mechanism_get_priority (void)
{
/* We prefer ANONYMOUS to most other mechanism (such as DBUS_COOKIE_SHA1) but not to EXTERNAL */
return 50;
}
static const gchar *
mechanism_get_name (void)
{
return "ANONYMOUS";
}
static gboolean
mechanism_is_supported (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), FALSE);
return TRUE;
}
static gchar *
mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
return NULL;
}
static gchar *
mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
return NULL;
}
/* ---------------------------------------------------------------------------------------------------- */
static GDBusAuthMechanismState
mechanism_server_get_state (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return m->priv->state;
}
static void
mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
if (initial_response != NULL)
g_debug ("ANONYMOUS: initial_response was `%s'", initial_response);
m->priv->is_server = TRUE;
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
}
static void
mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
/* can never end up here because we are never in the WAITING_FOR_DATA state */
g_assert_not_reached ();
}
static gchar *
mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
g_assert_not_reached ();
return NULL;
}
static gchar *
mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
/* can never end up here because we are never in the REJECTED state */
g_assert_not_reached ();
return NULL;
}
static void
mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
m->priv->is_server = FALSE;
}
/* ---------------------------------------------------------------------------------------------------- */
static GDBusAuthMechanismState
mechanism_client_get_state (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return m->priv->state;
}
static gchar *
mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
m->priv->is_client = TRUE;
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
*out_initial_response_len = -1;
/* just return our library name and version */
return g_strdup ("GDBus 0.1");
}
static void
mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
/* can never end up here because we are never in the WAITING_FOR_DATA state */
g_assert_not_reached ();
}
static gchar *
mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
g_assert_not_reached ();
return NULL;
}
static void
mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
m->priv->is_client = FALSE;
}
/* ---------------------------------------------------------------------------------------------------- */

View File

@ -0,0 +1,82 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#if !defined (GIO_COMPILATION)
#error "gdbusauthmechanismanon.h is a private header file."
#endif
#ifndef __G_DBUS_AUTH_MECHANISM_ANON_H__
#define __G_DBUS_AUTH_MECHANISM_ANON_H__
#include <gio/giotypes.h>
#include <gio/gdbusauthmechanism.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_MECHANISM_ANON (_g_dbus_auth_mechanism_anon_get_type ())
#define G_DBUS_AUTH_MECHANISM_ANON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnon))
#define G_DBUS_AUTH_MECHANISM_ANON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnonClass))
#define G_DBUS_AUTH_MECHANISM_ANON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnonClass))
#define G_IS_DBUS_AUTH_MECHANISM_ANON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON))
#define G_IS_DBUS_AUTH_MECHANISM_ANON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_ANON))
typedef struct _GDBusAuthMechanismAnon GDBusAuthMechanismAnon;
typedef struct _GDBusAuthMechanismAnonClass GDBusAuthMechanismAnonClass;
typedef struct _GDBusAuthMechanismAnonPrivate GDBusAuthMechanismAnonPrivate;
struct _GDBusAuthMechanismAnonClass
{
/*< private >*/
GDBusAuthMechanismClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
void (*_g_reserved9) (void);
void (*_g_reserved10) (void);
void (*_g_reserved11) (void);
void (*_g_reserved12) (void);
void (*_g_reserved13) (void);
void (*_g_reserved14) (void);
void (*_g_reserved15) (void);
void (*_g_reserved16) (void);
};
struct _GDBusAuthMechanismAnon
{
GDBusAuthMechanism parent_instance;
GDBusAuthMechanismAnonPrivate *priv;
};
GType _g_dbus_auth_mechanism_anon_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_DBUS_AUTH_MECHANISM_ANON_H__ */

View File

@ -0,0 +1,416 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <glib/gi18n.h>
#include "gdbusauthmechanismexternal.h"
#include "gcredentials.h"
#include "gdbuserror.h"
#include "gioenumtypes.h"
#ifdef G_OS_UNIX
#include <sys/types.h>
#include <unistd.h>
#endif
struct _GDBusAuthMechanismExternalPrivate
{
gboolean is_client;
gboolean is_server;
GDBusAuthMechanismState state;
};
static gint mechanism_get_priority (void);
static const gchar *mechanism_get_name (void);
static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len);
static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len);
static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
/* ---------------------------------------------------------------------------------------------------- */
G_DEFINE_TYPE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM);
/* ---------------------------------------------------------------------------------------------------- */
static void
_g_dbus_auth_mechanism_external_finalize (GObject *object)
{
//GDBusAuthMechanismExternal *mechanism = G_DBUS_AUTH_MECHANISM_EXTERNAL (object);
if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize != NULL)
G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize (object);
}
static void
_g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *klass)
{
GObjectClass *gobject_class;
GDBusAuthMechanismClass *mechanism_class;
g_type_class_add_private (klass, sizeof (GDBusAuthMechanismExternalPrivate));
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = _g_dbus_auth_mechanism_external_finalize;
mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
mechanism_class->get_name = mechanism_get_name;
mechanism_class->get_priority = mechanism_get_priority;
mechanism_class->is_supported = mechanism_is_supported;
mechanism_class->encode_data = mechanism_encode_data;
mechanism_class->decode_data = mechanism_decode_data;
mechanism_class->server_get_state = mechanism_server_get_state;
mechanism_class->server_initiate = mechanism_server_initiate;
mechanism_class->server_data_receive = mechanism_server_data_receive;
mechanism_class->server_data_send = mechanism_server_data_send;
mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
mechanism_class->server_shutdown = mechanism_server_shutdown;
mechanism_class->client_get_state = mechanism_client_get_state;
mechanism_class->client_initiate = mechanism_client_initiate;
mechanism_class->client_data_receive = mechanism_client_data_receive;
mechanism_class->client_data_send = mechanism_client_data_send;
mechanism_class->client_shutdown = mechanism_client_shutdown;
}
static void
_g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
{
mechanism->priv = G_TYPE_INSTANCE_GET_PRIVATE (mechanism,
G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL,
GDBusAuthMechanismExternalPrivate);
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
mechanism_is_supported (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE);
/* This mechanism is only available if credentials has been exchanged */
if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
return TRUE;
else
return FALSE;
}
static gint
mechanism_get_priority (void)
{
/* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
return 100;
}
static const gchar *
mechanism_get_name (void)
{
return "EXTERNAL";
}
static gchar *
mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
return NULL;
}
static gchar *
mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
return NULL;
}
/* ---------------------------------------------------------------------------------------------------- */
static GDBusAuthMechanismState
mechanism_server_get_state (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return m->priv->state;
}
static gboolean
data_matches_credentials (const gchar *data,
GCredentials *credentials)
{
gboolean match;
match = FALSE;
if (credentials == NULL)
goto out;
if (data == NULL || strlen (data) == 0)
goto out;
#if defined(G_OS_UNIX)
{
gint64 alleged_uid;
gchar *endp;
/* on UNIX, this is the uid as a string in base 10 */
alleged_uid = g_ascii_strtoll (data, &endp, 10);
if (*endp == '\0')
{
if (g_credentials_has_unix_user (credentials) &&
g_credentials_get_unix_user (credentials) == alleged_uid)
{
match = TRUE;
}
}
}
#elif defined(G_OS_WIN32)
{
const gchar *alleged_sid;
/* on Win32, this is the User SID */
alleged_sid = data;
if (g_credentials_has_windows_user (credentials) &&
g_strcmp0 (g_credentials_get_windows_user (credentials), alleged_sid) == 0)
{
match = TRUE;
}
}
#else
#error Dont know how to read credentials on this OS. Please implement.
#endif
out:
return match;
}
static void
mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
m->priv->is_server = TRUE;
if (initial_response != NULL)
{
if (data_matches_credentials (initial_response, _g_dbus_auth_mechanism_get_credentials (mechanism)))
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
}
else
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
}
}
else
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
}
}
static void
mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
if (data_matches_credentials (data, _g_dbus_auth_mechanism_get_credentials (mechanism)))
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
}
else
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
}
}
static gchar *
mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
g_assert_not_reached ();
return NULL;
}
static gchar *
mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
/* can never end up here because we are never in the REJECTED state */
g_assert_not_reached ();
return NULL;
}
static void
mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
m->priv->is_server = FALSE;
}
/* ---------------------------------------------------------------------------------------------------- */
static GDBusAuthMechanismState
mechanism_client_get_state (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return m->priv->state;
}
static gchar *
mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
gchar *initial_response;
GCredentials *credentials;
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
m->priv->is_client = TRUE;
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
*out_initial_response_len = -1;
credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
g_assert (credentials != NULL);
/* return the uid */
#if defined(G_OS_UNIX)
initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, g_credentials_get_unix_user (credentials));
#elif defined(G_OS_WIN32)
initial_response = g_strdup_printf ("%s", g_credentials_get_windows_user ());
#else
#warning Dont know how to send credentials on this OS. Please implement.
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
#endif
return initial_response;
}
static void
mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
/* can never end up here because we are never in the WAITING_FOR_DATA state */
g_assert_not_reached ();
}
static gchar *
mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
g_assert_not_reached ();
return NULL;
}
static void
mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
m->priv->is_client = FALSE;
}
/* ---------------------------------------------------------------------------------------------------- */

View File

@ -0,0 +1,82 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#if !defined (GIO_COMPILATION)
#error "gdbusauthmechanismexternal.h is a private header file."
#endif
#ifndef __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__
#define __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__
#include <gio/giotypes.h>
#include <gio/gdbusauthmechanism.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL (_g_dbus_auth_mechanism_external_get_type ())
#define G_DBUS_AUTH_MECHANISM_EXTERNAL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternal))
#define G_DBUS_AUTH_MECHANISM_EXTERNAL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternalClass))
#define G_DBUS_AUTH_MECHANISM_EXTERNAL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternalClass))
#define G_IS_DBUS_AUTH_MECHANISM_EXTERNAL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL))
#define G_IS_DBUS_AUTH_MECHANISM_EXTERNAL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL))
typedef struct _GDBusAuthMechanismExternal GDBusAuthMechanismExternal;
typedef struct _GDBusAuthMechanismExternalClass GDBusAuthMechanismExternalClass;
typedef struct _GDBusAuthMechanismExternalPrivate GDBusAuthMechanismExternalPrivate;
struct _GDBusAuthMechanismExternalClass
{
/*< private >*/
GDBusAuthMechanismClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
void (*_g_reserved9) (void);
void (*_g_reserved10) (void);
void (*_g_reserved11) (void);
void (*_g_reserved12) (void);
void (*_g_reserved13) (void);
void (*_g_reserved14) (void);
void (*_g_reserved15) (void);
void (*_g_reserved16) (void);
};
struct _GDBusAuthMechanismExternal
{
GDBusAuthMechanism parent_instance;
GDBusAuthMechanismExternalPrivate *priv;
};
GType _g_dbus_auth_mechanism_external_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__ */

1216
gio/gdbusauthmechanismsha1.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#if !defined (GIO_COMPILATION)
#error "gdbusauthmechanismsha1.h is a private header file."
#endif
#ifndef __G_DBUS_AUTH_MECHANISM_SHA1_H__
#define __G_DBUS_AUTH_MECHANISM_SHA1_H__
#include <gio/giotypes.h>
#include <gio/gdbusauthmechanism.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_MECHANISM_SHA1 (_g_dbus_auth_mechanism_sha1_get_type ())
#define G_DBUS_AUTH_MECHANISM_SHA1(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1))
#define G_DBUS_AUTH_MECHANISM_SHA1_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1Class))
#define G_DBUS_AUTH_MECHANISM_SHA1_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1Class))
#define G_IS_DBUS_AUTH_MECHANISM_SHA1(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1))
#define G_IS_DBUS_AUTH_MECHANISM_SHA1_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_SHA1))
typedef struct _GDBusAuthMechanismSha1 GDBusAuthMechanismSha1;
typedef struct _GDBusAuthMechanismSha1Class GDBusAuthMechanismSha1Class;
typedef struct _GDBusAuthMechanismSha1Private GDBusAuthMechanismSha1Private;
struct _GDBusAuthMechanismSha1Class
{
/*< private >*/
GDBusAuthMechanismClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
void (*_g_reserved9) (void);
void (*_g_reserved10) (void);
void (*_g_reserved11) (void);
void (*_g_reserved12) (void);
void (*_g_reserved13) (void);
void (*_g_reserved14) (void);
void (*_g_reserved15) (void);
void (*_g_reserved16) (void);
};
struct _GDBusAuthMechanismSha1
{
GDBusAuthMechanism parent_instance;
GDBusAuthMechanismSha1Private *priv;
};
GType _g_dbus_auth_mechanism_sha1_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_DBUS_AUTH_MECHANISM_SHA1_H__ */

218
gio/gdbusauthobserver.c Normal file
View File

@ -0,0 +1,218 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <glib/gi18n.h>
#include "gdbusauthobserver.h"
#include "gio-marshal.h"
#include "gcredentials.h"
#include "gioenumtypes.h"
#include "giostream.h"
/**
* SECTION:gdbusauthobserver
* @short_description: Object used for authenticating connections
* @include: gdbus/gdbus.h
*
* The #GDBusAuthObserver type provides a mechanism for participating
* in how a #GDBusServer (or a #GDBusConnection) authenticates remote
* peers. Simply instantiate a #GDBusAuthObserver and connect to the
* signals you are interested in. Note that new signals may be added
* in the future
*
* For example, if you only want to allow D-Bus connections from
* processes owned by the same uid as the server, you would do this:
* <example id="auth-observer"><title>Controlling Authentication</title><programlisting>
* static gboolean
* on_deny_authenticated_peer (GDBusAuthObserver *observer,
* GIOStream *stream,
* GCredentials *credentials,
* gpointer user_data)
* {
* gboolean deny;
* deny = TRUE;
* if (credentials != NULL &&
* g_credentials_has_unix_user (credentials) &&
* g_credentials_get_unix_user (credentials) == getuid ())
* deny = FALSE;
* return deny;
* }
*
* static gboolean
* on_new_connection (GDBusServer *server,
* GDBusConnection *connection,
* gpointer user_data)
* {
* /<!-- -->* Guaranteed here that @connection is from a process owned by the same user *<!-- -->/
* }
*
* [...]
*
* GDBusAuthObserver *observer;
* GDBusServer *server;
* GError *error;
*
* error = NULL;
* observer = g_dbus_auth_observer_new ();
* server = g_dbus_server_new_sync ("unix:tmpdir=/tmp/my-app-name",
* G_DBUS_SERVER_FLAGS_NONE,
* observer,
* NULL, /<!-- -->* GCancellable *<!-- -->/
* &error);
* g_signal_connect (observer,
* "deny-authenticated-peer",
* G_CALLBACK (on_deny_authenticated_peer),
* NULL);
* g_signal_connect (server,
* "new-connection",
* G_CALLBACK (on_new_connection),
* NULL);
* g_object_unref (observer);
* g_dbus_server_start (server);
* </programlisting></example>
*/
struct _GDBusAuthObserverPrivate
{
gint foo;
};
enum
{
DENY_AUTHENTICATED_PEER_SIGNAL,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (GDBusAuthObserver, g_dbus_auth_observer, G_TYPE_OBJECT);
/* ---------------------------------------------------------------------------------------------------- */
static void
g_dbus_auth_observer_finalize (GObject *object)
{
//GDBusAuthObserver *observer = G_DBUS_AUTH_OBSERVER (object);
if (G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize != NULL)
G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize (object);
}
static gboolean
g_dbus_auth_observer_deny_authenticated_peer_real (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials)
{
return FALSE;
}
static void
g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_dbus_auth_observer_finalize;
klass->deny_authenticated_peer = g_dbus_auth_observer_deny_authenticated_peer_real;
/**
* GDBusAuthObserver::deny-authenticated-peer:
* @observer: The #GDBusAuthObserver emitting the signal.
* @stream: A #GIOStream for the #GDBusConnection.
* @credentials: Credentials received from the peer or %NULL.
*
* Emitted to check if a peer that is successfully authenticated
* should be denied.
*
* Returns: %TRUE if the peer should be denied, %FALSE otherwise.
*/
signals[DENY_AUTHENTICATED_PEER_SIGNAL] =
g_signal_new ("deny-authenticated-peer",
G_TYPE_DBUS_AUTH_OBSERVER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GDBusAuthObserverClass, deny_authenticated_peer),
g_signal_accumulator_true_handled,
NULL, /* accu_data */
_gio_marshal_BOOLEAN__OBJECT_OBJECT,
G_TYPE_BOOLEAN,
2,
G_TYPE_IO_STREAM,
G_TYPE_CREDENTIALS);
g_type_class_add_private (klass, sizeof (GDBusAuthObserverPrivate));
}
static void
g_dbus_auth_observer_init (GDBusAuthObserver *observer)
{
/* not used for now */
observer->priv = G_TYPE_INSTANCE_GET_PRIVATE (observer,
G_TYPE_DBUS_AUTH_OBSERVER,
GDBusAuthObserverPrivate);;
}
/**
* g_dbus_auth_observer_new:
*
* Creates a new #GDBusAuthObserver object.
*
* Returns: A #GDBusAuthObserver. Free with g_object_unref().
*/
GDBusAuthObserver *
g_dbus_auth_observer_new (void)
{
return g_object_new (G_TYPE_DBUS_AUTH_OBSERVER, NULL);
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_dbus_auth_observer_deny_authenticated_peer:
* @observer: A #GDBusAuthObserver.
* @stream: A #GIOStream for the #GDBusConnection.
* @credentials: Credentials received from the peer or %NULL.
*
* Emits the #GDBusAuthObserver::deny-authenticated-peer signal on @observer.
*
* Returns: %TRUE if the peer should be denied, %FALSE otherwise.
*/
gboolean
g_dbus_auth_observer_deny_authenticated_peer (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials)
{
gboolean denied;
denied = FALSE;
g_signal_emit (observer,
signals[DENY_AUTHENTICATED_PEER_SIGNAL],
0,
stream,
credentials,
&denied);
return denied;
}

100
gio/gdbusauthobserver.h Normal file
View File

@ -0,0 +1,100 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_AUTH_OBSERVER_H__
#define __G_DBUS_AUTH_OBSERVER_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_OBSERVER (g_dbus_auth_observer_get_type ())
#define G_DBUS_AUTH_OBSERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_OBSERVER, GDBusAuthObserver))
#define G_DBUS_AUTH_OBSERVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_OBSERVER, GDBusAuthObserverClass))
#define G_DBUS_AUTH_OBSERVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_OBSERVER, GDBusAuthObserverClass))
#define G_IS_DBUS_AUTH_OBSERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_OBSERVER))
#define G_IS_DBUS_AUTH_OBSERVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_OBSERVER))
typedef struct _GDBusAuthObserverClass GDBusAuthObserverClass;
typedef struct _GDBusAuthObserverPrivate GDBusAuthObserverPrivate;
/**
* GDBusAuthObserverClass:
* @deny_authenticated_peer: Signal class handler for the #GDBusAuthObserver::deny-authenticated-peer signal.
*
* Class structure for #GDBusAuthObserverClass.
*/
struct _GDBusAuthObserverClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
/* Signals */
gboolean (*deny_authenticated_peer) (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials);
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
void (*_g_reserved9) (void);
void (*_g_reserved10) (void);
void (*_g_reserved11) (void);
void (*_g_reserved12) (void);
void (*_g_reserved13) (void);
void (*_g_reserved14) (void);
void (*_g_reserved15) (void);
void (*_g_reserved16) (void);
};
/**
* GDBusAuthObserver:
*
* The #GDBusAuthObserver structure contains only private data and
* should only be accessed using the provided API.
*/
struct _GDBusAuthObserver
{
GObject parent_instance;
GDBusAuthObserverPrivate *priv;
};
GType g_dbus_auth_observer_get_type (void) G_GNUC_CONST;
GDBusAuthObserver *g_dbus_auth_observer_new (void);
gboolean g_dbus_auth_observer_deny_authenticated_peer (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials);
G_END_DECLS
#endif /* _G_DBUS_AUTH_OBSERVER_H__ */

5280
gio/gdbusconnection.c Normal file

File diff suppressed because it is too large Load Diff

467
gio/gdbusconnection.h Normal file
View File

@ -0,0 +1,467 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_CONNECTION_H__
#define __G_DBUS_CONNECTION_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_CONNECTION (g_dbus_connection_get_type ())
#define G_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_CONNECTION, GDBusConnection))
#define G_DBUS_CONNECTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_CONNECTION, GDBusConnectionClass))
#define G_DBUS_CONNECTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_CONNECTION, GDBusConnectionClass))
#define G_IS_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_CONNECTION))
#define G_IS_DBUS_CONNECTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_CONNECTION))
typedef struct _GDBusConnectionClass GDBusConnectionClass;
typedef struct _GDBusConnectionPrivate GDBusConnectionPrivate;
/**
* GDBusConnection:
*
* The #GDBusConnection structure contains only private data and
* should only be accessed using the provided API.
*/
struct _GDBusConnection
{
/*< private >*/
GObject parent_instance;
GDBusConnectionPrivate *priv;
};
/**
* GDBusConnectionClass:
* @closed: Signal class handler for the #GDBusConnection::closed signal.
*
* Class structure for #GDBusConnection.
*/
struct _GDBusConnectionClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
/* Signals */
void (*closed) (GDBusConnection *connection,
gboolean remote_peer_vanished,
GError *error);
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
};
GType g_dbus_connection_get_type (void) G_GNUC_CONST;
/* ---------------------------------------------------------------------------------------------------- */
void g_bus_get (GBusType bus_type,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDBusConnection *g_bus_get_finish (GAsyncResult *res,
GError **error);
GDBusConnection *g_bus_get_sync (GBusType bus_type,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
void g_dbus_connection_new (GIOStream *stream,
const gchar *guid,
GDBusConnectionFlags flags,
GDBusAuthObserver *auth_observer,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDBusConnection *g_dbus_connection_new_finish (GAsyncResult *res,
GError **error);
GDBusConnection *g_dbus_connection_new_sync (GIOStream *stream,
const gchar *guid,
GDBusConnectionFlags flags,
GDBusAuthObserver *auth_observer,
GCancellable *cancellable,
GError **error);
void g_dbus_connection_new_for_address (const gchar *address,
GDBusConnectionFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDBusConnection *g_dbus_connection_new_for_address_finish (GAsyncResult *res,
GError **error);
GDBusConnection *g_dbus_connection_new_for_address_sync (const gchar *address,
GDBusConnectionFlags flags,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
gboolean g_dbus_connection_is_closed (GDBusConnection *connection);
void g_dbus_connection_close (GDBusConnection *connection);
GIOStream *g_dbus_connection_get_stream (GDBusConnection *connection);
const gchar *g_dbus_connection_get_guid (GDBusConnection *connection);
const gchar *g_dbus_connection_get_unique_name (GDBusConnection *connection);
GCredentials *g_dbus_connection_get_peer_credentials (GDBusConnection *connection);
gboolean g_dbus_connection_get_exit_on_close (GDBusConnection *connection);
void g_dbus_connection_set_exit_on_close (GDBusConnection *connection,
gboolean exit_on_close);
GDBusCapabilityFlags g_dbus_connection_get_capabilities (GDBusConnection *connection);
/* ---------------------------------------------------------------------------------------------------- */
gboolean g_dbus_connection_send_message (GDBusConnection *connection,
GDBusMessage *message,
volatile guint32 *out_serial,
GError **error);
void g_dbus_connection_send_message_with_reply (GDBusConnection *connection,
GDBusMessage *message,
gint timeout_msec,
volatile guint32 *out_serial,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDBusMessage *g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection,
GAsyncResult *res,
GError **error);
GDBusMessage *g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection,
GDBusMessage *message,
gint timeout_msec,
volatile guint32 *out_serial,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
gboolean g_dbus_connection_emit_signal (GDBusConnection *connection,
const gchar *destination_bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
GError **error);
void g_dbus_connection_invoke_method (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusInvokeMethodFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GVariant *g_dbus_connection_invoke_method_finish (GDBusConnection *connection,
GAsyncResult *res,
GError **error);
GVariant *g_dbus_connection_invoke_method_sync (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusInvokeMethodFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
/**
* GDBusInterfaceMethodCallFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that the method was invoked on.
* @interface_name: The D-Bus interface name the method was invoked on.
* @method_name: The name of the method that was invoked.
* @parameters: A #GVariant tuple with parameters.
* @invocation: A #GDBusMethodInvocation object that can be used to return a value or error.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
*
* The type of the @method_call function in #GDBusInterfaceVTable.
*/
typedef void (*GDBusInterfaceMethodCallFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data);
/**
* GDBusInterfaceGetPropertyFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that the method was invoked on.
* @interface_name: The D-Bus interface name for the property.
* @property_name: The name of the property to get the value of.
* @error: Return location for error.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
*
* The type of the @get_property function in #GDBusInterfaceVTable.
*
* Returns: A newly-allocated #GVariant with the value for @property_name or %NULL if @error is set.
*/
typedef GVariant *(*GDBusInterfaceGetPropertyFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data);
/**
* GDBusInterfaceSetPropertyFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that the method was invoked on.
* @interface_name: The D-Bus interface name for the property.
* @property_name: The name of the property to get the value of.
* @value: The value to set the property to.
* @error: Return location for error.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
*
* The type of the @set_property function in #GDBusInterfaceVTable.
*
* Returns: %TRUE if the property was set to @value, %FALSE if @error is set.
*/
typedef gboolean (*GDBusInterfaceSetPropertyFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error,
gpointer user_data);
/**
* GDBusInterfaceVTable:
* @method_call: Function for handling incoming method calls.
* @get_property: Function for getting a property.
* @set_property: Function for setting a property.
*
* Virtual table for handling properties and method calls for a D-Bus
* interface.
*
* If you want to handle getting/setting D-Bus properties asynchronously, simply
* register an object with the <literal>org.freedesktop.DBus.Properties</literal>
* D-Bus interface using g_dbus_connection_register_object().
*/
struct _GDBusInterfaceVTable
{
GDBusInterfaceMethodCallFunc method_call;
GDBusInterfaceGetPropertyFunc get_property;
GDBusInterfaceSetPropertyFunc set_property;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
};
guint g_dbus_connection_register_object (GDBusConnection *connection,
const gchar *object_path,
const gchar *interface_name,
const GDBusInterfaceInfo *introspection_data,
const GDBusInterfaceVTable *vtable,
gpointer user_data,
GDestroyNotify user_data_free_func,
GError **error);
gboolean g_dbus_connection_unregister_object (GDBusConnection *connection,
guint registration_id);
/* ---------------------------------------------------------------------------------------------------- */
/**
* GDBusSubtreeEnumerateFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
*
* The type of the @enumerate function in #GDBusSubtreeVTable.
*
* Returns: A newly allocated array of strings for node names that are children of @object_path.
*/
typedef gchar** (*GDBusSubtreeEnumerateFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
gpointer user_data);
/**
* GDBusSubtreeIntrospectFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
* @node: A node that is a child of @object_path (relative to @object_path) or <quote>/</quote> for the root of the subtree.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
*
* The type of the @introspect function in #GDBusSubtreeVTable.
*
* Returns: A newly-allocated #GPtrArray with pointers to #GDBusInterfaceInfo describing
* the interfaces implemented by @node.
*/
typedef GPtrArray *(*GDBusSubtreeIntrospectFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *node,
gpointer user_data);
/**
* GDBusSubtreeDispatchFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
* @interface_name: The D-Bus interface name that the method call or property access is for.
* @node: A node that is a child of @object_path (relative to @object_path) or <quote>/</quote> for the root of the subtree.
* @out_user_data: Return location for user data to pass to functions in the returned #GDBusInterfaceVTable (never %NULL).
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
*
* The type of the @dispatch function in #GDBusSubtreeVTable.
*
* Returns: A #GDBusInterfaceVTable or %NULL if you don't want to handle the methods.
*/
typedef const GDBusInterfaceVTable * (*GDBusSubtreeDispatchFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *node,
gpointer *out_user_data,
gpointer user_data);
/**
* GDBusSubtreeVTable:
* @enumerate: Function for enumerating child nodes.
* @introspect: Function for introspecting a child node.
* @dispatch: Function for dispatching a remote call on a child node.
*
* Virtual table for handling subtrees registered with g_dbus_connection_register_subtree().
*/
struct _GDBusSubtreeVTable
{
GDBusSubtreeEnumerateFunc enumerate;
GDBusSubtreeIntrospectFunc introspect;
GDBusSubtreeDispatchFunc dispatch;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
};
guint g_dbus_connection_register_subtree (GDBusConnection *connection,
const gchar *object_path,
const GDBusSubtreeVTable *vtable,
GDBusSubtreeFlags flags,
gpointer user_data,
GDestroyNotify user_data_free_func,
GError **error);
gboolean g_dbus_connection_unregister_subtree (GDBusConnection *connection,
guint registration_id);
/* ---------------------------------------------------------------------------------------------------- */
/**
* GDBusSignalCallback:
* @connection: A #GDBusConnection.
* @sender_name: The unique bus name of the sender of the signal.
* @object_path: The object path that the signal was emitted on.
* @interface_name: The name of the signal.
* @signal_name: The name of the signal.
* @parameters: A #GVariant tuple with parameters for the signal.
* @user_data: User data passed when subscribing to the signal.
*
* Signature for callback function used in g_dbus_connection_signal_subscribe().
*/
typedef void (*GDBusSignalCallback) (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data);
guint g_dbus_connection_signal_subscribe (GDBusConnection *connection,
const gchar *sender,
const gchar *interface_name,
const gchar *member,
const gchar *object_path,
const gchar *arg0,
GDBusSignalCallback callback,
gpointer user_data,
GDestroyNotify user_data_free_func);
void g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
guint subscription_id);
/* ---------------------------------------------------------------------------------------------------- */
/**
* GDBusMessageFilterFunction:
* @connection: A #GDBusConnection.
* @message: A #GDBusMessage.
* @user_data: User data passed when adding the filter.
*
* Signature for function used in g_dbus_connection_add_filter().
*
* Returns: %TRUE if the filter handled @message, %FALSE to let other
* handlers run.
*/
typedef gboolean (*GDBusMessageFilterFunction) (GDBusConnection *connection,
GDBusMessage *message,
gpointer user_data);
guint g_dbus_connection_add_filter (GDBusConnection *connection,
GDBusMessageFilterFunction filter_function,
gpointer user_data,
GDestroyNotify user_data_free_func);
void g_dbus_connection_remove_filter (GDBusConnection *connection,
guint filter_id);
/* ---------------------------------------------------------------------------------------------------- */
G_END_DECLS
#endif /* __G_DBUS_CONNECTION_H__ */

847
gio/gdbuserror.c Normal file
View File

@ -0,0 +1,847 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <glib/gi18n.h>
#include "gdbuserror.h"
#include "gioenums.h"
#include "gioenumtypes.h"
#include "gioerror.h"
#include "gdbusprivate.h"
/**
* SECTION:gdbuserror
* @title: GDBusError
* @short_description: Mapping D-Bus errors to and from #GError
* @include: gdbus/gdbus.h
*
* All facilities that return errors from remote methods (such as
* g_dbus_connection_invoke_method_sync()) use #GError to represent
* both D-Bus errors (e.g. errors returned from the other peer) and
* locally in-process generated errors.
*
* To check if a returned #GError is an error from a remote peer, use
* g_dbus_error_is_remote_error(). To get the actual D-Bus error name,
* use g_dbus_error_get_remote_error(). Before presenting an error,
* always use g_dbus_error_strip_remote_error().
*
* In addition, facilities used to return errors to a remote peer also
* use #GError. See g_dbus_method_invocation_return_error() for
* discussion about how the D-Bus error name is set.
*
* Applications can associate a #GError error domain with a set of D-Bus errors in order to
* automatically map from D-Bus errors to #GError and back. This
* is typically done in the function returning the #GQuark for the
* error domain:
* <example id="error-registration"><title>Error Registration</title><programlisting>
* /<!-- -->* foo-bar-error.h: *<!-- -->/
*
* #define FOO_BAR_ERROR (foo_bar_error_quark ())
* GQuark foo_bar_error_quark (void);
*
* typedef enum
* {
* FOO_BAR_ERROR_FAILED,
* FOO_BAR_ERROR_ANOTHER_ERROR,
* FOO_BAR_ERROR_SOME_THIRD_ERROR,
* } FooBarError;
*
* /<!-- -->* foo-bar-error.c: *<!-- -->/
*
* static const GDBusErrorEntry foo_bar_error_entries[] =
* {
* {FOO_BAR_ERROR_FAILED, "org.project.Foo.Bar.Error.Failed"},
* {FOO_BAR_ERROR_ANOTHER_ERROR, "org.project.Foo.Bar.Error.AnotherError"},
* {FOO_BAR_ERROR_SOME_THIRD_ERROR, "org.project.Foo.Bar.Error.SomeThirdError"},
* };
*
* GQuark
* foo_bar_error_quark (void)
* {
* static volatile gsize quark_volatile = 0;
* g_dbus_error_register_error_domain ("foo-bar-error-quark",
* &quark_volatile,
* foo_bar_error_entries,
* G_N_ELEMENTS (foo_bar_error_entries));
* G_STATIC_ASSERT (G_N_ELEMENTS (foo_bar_error_entries) - 1 == FOO_BAR_ERROR_SOME_THIRD_ERROR);
* return (GQuark) quark_volatile;
* }
* </programlisting></example>
* With this setup, a D-Bus peer can transparently pass e.g. %FOO_BAR_ERROR_ANOTHER_ERROR and
* other peers will see the D-Bus error name <literal>org.project.Foo.Bar.Error.AnotherError</literal>.
* If the other peer is using GDBus, the peer will see also %FOO_BAR_ERROR_ANOTHER_ERROR instead
* of %G_IO_ERROR_DBUS_ERROR. Note that GDBus clients can still recover
* <literal>org.project.Foo.Bar.Error.AnotherError</literal> using g_dbus_error_get_remote_error().
*
* Note that errors in the %G_DBUS_ERROR error domain is intended only
* for returning errors from a remote message bus process. Errors
* generated locally in-process by e.g. #GDBusConnection is from the
* %G_IO_ERROR domain.
*/
static const GDBusErrorEntry g_dbus_error_entries[] =
{
{G_DBUS_ERROR_FAILED, "org.freedesktop.DBus.Error.Failed"},
{G_DBUS_ERROR_NO_MEMORY, "org.freedesktop.DBus.Error.NoMemory"},
{G_DBUS_ERROR_SERVICE_UNKNOWN, "org.freedesktop.DBus.Error.ServiceUnknown"},
{G_DBUS_ERROR_NAME_HAS_NO_OWNER, "org.freedesktop.DBus.Error.NameHasNoOwner"},
{G_DBUS_ERROR_NO_REPLY, "org.freedesktop.DBus.Error.NoReply"},
{G_DBUS_ERROR_IO_ERROR, "org.freedesktop.DBus.Error.IOError"},
{G_DBUS_ERROR_BAD_ADDRESS, "org.freedesktop.DBus.Error.BadAddress"},
{G_DBUS_ERROR_NOT_SUPPORTED, "org.freedesktop.DBus.Error.NotSupported"},
{G_DBUS_ERROR_LIMITS_EXCEEDED, "org.freedesktop.DBus.Error.LimitsExceeded"},
{G_DBUS_ERROR_ACCESS_DENIED, "org.freedesktop.DBus.Error.AccessDenied"},
{G_DBUS_ERROR_AUTH_FAILED, "org.freedesktop.DBus.Error.AuthFailed"},
{G_DBUS_ERROR_NO_SERVER, "org.freedesktop.DBus.Error.NoServer"},
{G_DBUS_ERROR_TIMEOUT, "org.freedesktop.DBus.Error.Timeout"},
{G_DBUS_ERROR_NO_NETWORK, "org.freedesktop.DBus.Error.NoNetwork"},
{G_DBUS_ERROR_ADDRESS_IN_USE, "org.freedesktop.DBus.Error.AddressInUse"},
{G_DBUS_ERROR_DISCONNECTED, "org.freedesktop.DBus.Error.Disconnected"},
{G_DBUS_ERROR_INVALID_ARGS, "org.freedesktop.DBus.Error.InvalidArgs"},
{G_DBUS_ERROR_FILE_NOT_FOUND, "org.freedesktop.DBus.Error.FileNotFound"},
{G_DBUS_ERROR_FILE_EXISTS, "org.freedesktop.DBus.Error.FileExists"},
{G_DBUS_ERROR_UNKNOWN_METHOD, "org.freedesktop.DBus.Error.UnknownMethod"},
{G_DBUS_ERROR_TIMED_OUT, "org.freedesktop.DBus.Error.TimedOut"},
{G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, "org.freedesktop.DBus.Error.MatchRuleNotFound"},
{G_DBUS_ERROR_MATCH_RULE_INVALID, "org.freedesktop.DBus.Error.MatchRuleInvalid"},
{G_DBUS_ERROR_SPAWN_EXEC_FAILED, "org.freedesktop.DBus.Error.Spawn.ExecFailed"},
{G_DBUS_ERROR_SPAWN_FORK_FAILED, "org.freedesktop.DBus.Error.Spawn.ForkFailed"},
{G_DBUS_ERROR_SPAWN_CHILD_EXITED, "org.freedesktop.DBus.Error.Spawn.ChildExited"},
{G_DBUS_ERROR_SPAWN_CHILD_SIGNALED, "org.freedesktop.DBus.Error.Spawn.ChildSignaled"},
{G_DBUS_ERROR_SPAWN_FAILED, "org.freedesktop.DBus.Error.Spawn.Failed"},
{G_DBUS_ERROR_SPAWN_SETUP_FAILED, "org.freedesktop.DBus.Error.Spawn.FailedToSetup"},
{G_DBUS_ERROR_SPAWN_CONFIG_INVALID, "org.freedesktop.DBus.Error.Spawn.ConfigInvalid"},
{G_DBUS_ERROR_SPAWN_SERVICE_INVALID, "org.freedesktop.DBus.Error.Spawn.ServiceNotValid"},
{G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND, "org.freedesktop.DBus.Error.Spawn.ServiceNotFound"},
{G_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID, "org.freedesktop.DBus.Error.Spawn.PermissionsInvalid"},
{G_DBUS_ERROR_SPAWN_FILE_INVALID, "org.freedesktop.DBus.Error.Spawn.FileInvalid"},
{G_DBUS_ERROR_SPAWN_NO_MEMORY, "org.freedesktop.DBus.Error.Spawn.NoMemory"},
{G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "org.freedesktop.DBus.Error.UnixProcessIdUnknown"},
{G_DBUS_ERROR_INVALID_SIGNATURE, "org.freedesktop.DBus.Error.InvalidSignature"},
{G_DBUS_ERROR_INVALID_FILE_CONTENT, "org.freedesktop.DBus.Error.InvalidFileContent"},
{G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"},
{G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN, "org.freedesktop.DBus.Error.AdtAuditDataUnknown"},
{G_DBUS_ERROR_OBJECT_PATH_IN_USE, "org.freedesktop.DBus.Error.ObjectPathInUse"},
};
GQuark
g_dbus_error_quark (void)
{
static volatile gsize quark_volatile = 0;
g_dbus_error_register_error_domain ("g-dbus-error-quark",
&quark_volatile,
g_dbus_error_entries,
G_N_ELEMENTS (g_dbus_error_entries));
G_STATIC_ASSERT (G_N_ELEMENTS (g_dbus_error_entries) - 1 == G_DBUS_ERROR_OBJECT_PATH_IN_USE);
return (GQuark) quark_volatile;
}
/**
* g_dbus_error_register_error_domain:
* @error_domain_quark_name: The error domain name.
* @quark_volatile: A pointer where to store the #GQuark.
* @entries: A pointer to @num_entries #GDBusErrorEntry struct items.
* @num_entries: Number of items to register.
*
* Helper function for associating a #GError error domain with D-Bus error names.
*/
void
g_dbus_error_register_error_domain (const gchar *error_domain_quark_name,
volatile gsize *quark_volatile,
const GDBusErrorEntry *entries,
guint num_entries)
{
g_return_if_fail (error_domain_quark_name != NULL);
g_return_if_fail (quark_volatile != NULL);
g_return_if_fail (entries != NULL);
g_return_if_fail (num_entries > 0);
if (g_once_init_enter (quark_volatile))
{
guint n;
GQuark quark;
quark = g_quark_from_static_string (error_domain_quark_name);
for (n = 0; n < num_entries; n++)
{
g_warn_if_fail (g_dbus_error_register_error (quark,
entries[n].error_code,
entries[n].dbus_error_name));
}
g_once_init_leave (quark_volatile, quark);
}
}
static gboolean
_g_dbus_error_decode_gerror (const gchar *dbus_name,
GQuark *out_error_domain,
gint *out_error_code)
{
gboolean ret;
guint n;
GString *s;
gchar *domain_quark_string;
ret = FALSE;
s = NULL;
if (g_str_has_prefix (dbus_name, "org.gtk.GDBus.UnmappedGError.Quark._"))
{
s = g_string_new (NULL);
for (n = sizeof "org.gtk.GDBus.UnmappedGError.Quark._" - 1;
dbus_name[n] != '.' && dbus_name[n] != '\0';
n++)
{
if (g_ascii_isalnum (dbus_name[n]))
{
g_string_append_c (s, dbus_name[n]);
}
else if (dbus_name[n] == '_')
{
guint nibble_top;
guint nibble_bottom;
n++;
nibble_top = dbus_name[n];
if (nibble_top >= '0' && nibble_top <= '9')
nibble_top -= '0';
else if (nibble_top >= 'a' && nibble_top <= 'f')
nibble_top -= ('a' - 10);
else
goto not_mapped;
n++;
nibble_bottom = dbus_name[n];
if (nibble_bottom >= '0' && nibble_bottom <= '9')
nibble_bottom -= '0';
else if (nibble_bottom >= 'a' && nibble_bottom <= 'f')
nibble_bottom -= ('a' - 10);
else
goto not_mapped;
g_string_append_c (s, (nibble_top<<4) | nibble_bottom);
}
else
{
goto not_mapped;
}
}
if (!g_str_has_prefix (dbus_name + n, ".Code"))
goto not_mapped;
domain_quark_string = g_string_free (s, FALSE);
s = NULL;
if (out_error_domain != NULL)
*out_error_domain = g_quark_from_string (domain_quark_string);
g_free (domain_quark_string);
if (out_error_code != NULL)
*out_error_code = atoi (dbus_name + n + sizeof ".Code" - 1);
ret = TRUE;
}
not_mapped:
if (s != NULL)
g_string_free (s, TRUE);
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
GQuark error_domain;
gint error_code;
} QuarkCodePair;
static guint
quark_code_pair_hash_func (const QuarkCodePair *pair)
{
gint val;
val = pair->error_domain + pair->error_code;
return g_int_hash (&val);
}
static gboolean
quark_code_pair_equal_func (const QuarkCodePair *a,
const QuarkCodePair *b)
{
return (a->error_domain == b->error_domain) && (a->error_code == b->error_code);
}
typedef struct
{
QuarkCodePair pair;
gchar *dbus_error_name;
} RegisteredError;
static void
registered_error_free (RegisteredError *re)
{
g_free (re->dbus_error_name);
g_free (re);
}
G_LOCK_DEFINE_STATIC (error_lock);
/* maps from QuarkCodePair* -> RegisteredError* */
static GHashTable *quark_code_pair_to_re = NULL;
/* maps from gchar* -> RegisteredError* */
static GHashTable *dbus_error_name_to_re = NULL;
/**
* g_dbus_error_register_error:
* @error_domain: A #GQuark for a error domain.
* @error_code: An error code.
* @dbus_error_name: A D-Bus error name.
*
* Creates an association to map between @dbus_error_name and
* #GError<!-- -->s specified by @error_domain and @error_code.
*
* This is typically done in the routine that returns the #GQuark for
* an error domain.
*
* Returns: %TRUE if the association was created, %FALSE if it already
* exists.
*/
gboolean
g_dbus_error_register_error (GQuark error_domain,
gint error_code,
const gchar *dbus_error_name)
{
gboolean ret;
QuarkCodePair pair;
RegisteredError *re;
g_return_val_if_fail (dbus_error_name != NULL, FALSE);
ret = FALSE;
G_LOCK (error_lock);
if (quark_code_pair_to_re == NULL)
{
g_assert (dbus_error_name_to_re == NULL); /* check invariant */
quark_code_pair_to_re = g_hash_table_new ((GHashFunc) quark_code_pair_hash_func,
(GEqualFunc) quark_code_pair_equal_func);
dbus_error_name_to_re = g_hash_table_new_full (g_str_hash,
g_str_equal,
NULL,
(GDestroyNotify) registered_error_free);
}
if (g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name) != NULL)
goto out;
pair.error_domain = error_domain;
pair.error_code = error_code;
if (g_hash_table_lookup (quark_code_pair_to_re, &pair) != NULL)
goto out;
re = g_new0 (RegisteredError, 1);
re->pair = pair;
re->dbus_error_name = g_strdup (dbus_error_name);
g_hash_table_insert (quark_code_pair_to_re, &(re->pair), re);
g_hash_table_insert (dbus_error_name_to_re, re->dbus_error_name, re);
ret = TRUE;
out:
G_UNLOCK (error_lock);
return ret;
}
/**
* g_dbus_error_unregister_error:
* @error_domain: A #GQuark for a error domain.
* @error_code: An error code.
* @dbus_error_name: A D-Bus error name.
*
* Destroys an association previously set up with g_dbus_error_register_error().
*
* Returns: %TRUE if the association was destroyed, %FALSE if it wasn't found.
*/
gboolean
g_dbus_error_unregister_error (GQuark error_domain,
gint error_code,
const gchar *dbus_error_name)
{
gboolean ret;
RegisteredError *re;
guint hash_size;
g_return_val_if_fail (dbus_error_name != NULL, FALSE);
ret = FALSE;
G_LOCK (error_lock);
if (dbus_error_name_to_re == NULL)
{
g_assert (quark_code_pair_to_re == NULL); /* check invariant */
goto out;
}
re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name);
if (re == NULL)
{
QuarkCodePair pair;
pair.error_domain = error_domain;
pair.error_code = error_code;
g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &pair) == NULL); /* check invariant */
goto out;
}
g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &(re->pair)) == re); /* check invariant */
g_warn_if_fail (g_hash_table_remove (quark_code_pair_to_re, &(re->pair)));
g_warn_if_fail (g_hash_table_remove (dbus_error_name_to_re, re));
/* destroy hashes if empty */
hash_size = g_hash_table_size (dbus_error_name_to_re);
if (hash_size == 0)
{
g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == 0); /* check invariant */
g_hash_table_unref (dbus_error_name_to_re);
dbus_error_name_to_re = NULL;
g_hash_table_unref (quark_code_pair_to_re);
quark_code_pair_to_re = NULL;
}
else
{
g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == hash_size); /* check invariant */
}
out:
G_UNLOCK (error_lock);
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_dbus_error_is_remote_error:
* @error: A #GError.
*
* Checks if @error represents an error received via D-Bus from a remote peer. If so,
* use g_dbus_error_get_remote_error() to get the name of the error.
*
* Returns: %TRUE if @error represents an error from a remote peer,
* %FALSE otherwise.
*/
gboolean
g_dbus_error_is_remote_error (const GError *error)
{
g_return_val_if_fail (error != NULL, FALSE);
return g_str_has_prefix (error->message, "GDBus.Error:");
}
/**
* g_dbus_error_get_remote_error:
* @error: A #GError.
*
* Gets the D-Bus error name used for @error, if any.
*
* This function is guaranteed to return a D-Bus error name for all #GError<!-- -->s returned from
* functions handling remote method calls (e.g. g_dbus_connection_invoke_method_finish())
* unless g_dbus_error_strip_remote_error() has been used on @error.
*
* Returns: An allocated string or %NULL if the D-Bus error name could not be found. Free with g_free().
*/
gchar *
g_dbus_error_get_remote_error (const GError *error)
{
RegisteredError *re;
gchar *ret;
g_return_val_if_fail (error != NULL, NULL);
/* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
_g_dbus_initialize ();
ret = NULL;
G_LOCK (error_lock);
re = NULL;
if (quark_code_pair_to_re != NULL)
{
QuarkCodePair pair;
pair.error_domain = error->domain;
pair.error_code = error->code;
g_assert (dbus_error_name_to_re != NULL); /* check invariant */
re = g_hash_table_lookup (quark_code_pair_to_re, &pair);
}
if (re != NULL)
{
ret = g_strdup (re->dbus_error_name);
}
else
{
if (g_str_has_prefix (error->message, "GDBus.Error:"))
{
const gchar *begin;
const gchar *end;
begin = error->message + sizeof ("GDBus.Error:") -1;
end = strstr (begin, ":");
if (end != NULL && end[1] == ' ')
{
ret = g_strndup (begin, end - begin);
}
}
}
G_UNLOCK (error_lock);
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_dbus_error_new_for_dbus_error:
* @dbus_error_name: D-Bus error name.
* @dbus_error_message: D-Bus error message.
*
* Creates a #GError based on the contents of @dbus_error_name and
* @dbus_error_message.
*
* Errors registered with g_dbus_error_register_error() will be looked
* up using @dbus_error_name and if a match is found, the error domain
* and code is used. Applications can use g_dbus_error_get_remote_error()
* to recover @dbus_error_name.
*
* If a match against a registered error is not found and the D-Bus
* error name is in a form as returned by g_dbus_error_encode_gerror()
* the error domain and code encoded in the name is used to
* create the #GError. Also, @dbus_error_name is added to the error message
* such that it can be recovered with g_dbus_error_get_remote_error().
*
* Otherwise, a #GError with the error code %G_IO_ERROR_DBUS_ERROR
* in the #G_IO_ERROR error domain is returned. Also, @dbus_error_name is
* added to the error message such that it can be recovered with
* g_dbus_error_get_remote_error().
*
* In all three cases, @dbus_error_name can always be recovered from the
* returned #GError using the g_dbus_error_get_remote_error() function
* (unless g_dbus_error_strip_remote_error() hasn't been used on the returned error).
*
* This function is typically only used in object mappings to prepare
* #GError instances for applications. Regular applications should not use
* it.
*
* Returns: An allocated #GError. Free with g_error_free().
*/
GError *
g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name,
const gchar *dbus_error_message)
{
GError *error;
RegisteredError *re;
g_return_val_if_fail (dbus_error_name != NULL, NULL);
g_return_val_if_fail (dbus_error_message != NULL, NULL);
/* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
_g_dbus_initialize ();
G_LOCK (error_lock);
re = NULL;
if (dbus_error_name_to_re != NULL)
{
g_assert (quark_code_pair_to_re != NULL); /* check invariant */
re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name);
}
if (re != NULL)
{
error = g_error_new (re->pair.error_domain,
re->pair.error_code,
"GDBus.Error:%s: %s",
dbus_error_name,
dbus_error_message);
}
else
{
GQuark error_domain = 0;
gint error_code = 0;
if (_g_dbus_error_decode_gerror (dbus_error_name,
&error_domain,
&error_code))
{
error = g_error_new (error_domain,
error_code,
"GDBus.Error:%s: %s",
dbus_error_name,
dbus_error_message);
}
else
{
error = g_error_new (G_IO_ERROR,
G_IO_ERROR_DBUS_ERROR,
"GDBus.Error:%s: %s",
dbus_error_name,
dbus_error_message);
}
}
G_UNLOCK (error_lock);
return error;
}
/**
* g_dbus_error_set_dbus_error:
* @error: A pointer to a #GError or %NULL.
* @dbus_error_name: D-Bus error name.
* @dbus_error_message: D-Bus error message.
* @format: printf()-style format to prepend to @dbus_error_message or %NULL.
* @...: Arguments for @format.
*
* Does nothing if @error is %NULL. Otherwise sets *@error to
* a new #GError created with g_dbus_error_new_for_dbus_error()
* with @dbus_error_message prepend with @format (unless %NULL).
*/
void
g_dbus_error_set_dbus_error (GError **error,
const gchar *dbus_error_name,
const gchar *dbus_error_message,
const gchar *format,
...)
{
g_return_if_fail (error == NULL || *error == NULL);
g_return_if_fail (dbus_error_name != NULL);
g_return_if_fail (dbus_error_message != NULL);
if (error == NULL)
return;
if (format == NULL)
{
*error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);
}
else
{
va_list var_args;
va_start (var_args, format);
g_dbus_error_set_dbus_error_valist (error,
dbus_error_name,
dbus_error_message,
format,
var_args);
va_end (var_args);
}
}
/**
* g_dbus_error_set_dbus_error_valist:
* @error: A pointer to a #GError or %NULL.
* @dbus_error_name: D-Bus error name.
* @dbus_error_message: D-Bus error message.
* @format: printf()-style format to prepend to @dbus_error_message or %NULL.
* @var_args: Arguments for @format.
*
* Like g_dbus_error_set_dbus_error() but intended for language bindings.
*/
void
g_dbus_error_set_dbus_error_valist (GError **error,
const gchar *dbus_error_name,
const gchar *dbus_error_message,
const gchar *format,
va_list var_args)
{
g_return_if_fail (error == NULL || *error == NULL);
g_return_if_fail (dbus_error_name != NULL);
g_return_if_fail (dbus_error_message != NULL);
if (error == NULL)
return;
if (format != NULL)
{
gchar *message;
gchar *s;
message = g_strdup_vprintf (format, var_args);
s = g_strdup_printf ("%s: %s", message, dbus_error_message);
*error = g_dbus_error_new_for_dbus_error (dbus_error_name, s);
g_free (s);
g_free (message);
}
else
{
*error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);
}
}
/**
* g_dbus_error_strip_remote_error:
* @error: A #GError.
*
* Looks for extra information in the error message used to recover
* the D-Bus error name and strips it if found. If stripped, the
* message field in @error will correspond exactly to what was
* received on the wire.
*
* This is typically used when presenting errors to the end user.
*
* Returns: %TRUE if information was stripped, %FALSE otherwise.
*/
gboolean
g_dbus_error_strip_remote_error (GError *error)
{
gboolean ret;
g_return_val_if_fail (error != NULL, FALSE);
ret = FALSE;
if (g_str_has_prefix (error->message, "GDBus.Error:"))
{
const gchar *begin;
const gchar *end;
gchar *new_message;
begin = error->message + sizeof ("GDBus.Error:") -1;
end = strstr (begin, ":");
if (end != NULL && end[1] == ' ')
{
new_message = g_strdup (end + 2);
g_free (error->message);
error->message = new_message;
ret = TRUE;
}
}
return ret;
}
/**
* g_dbus_error_encode_gerror:
* @error: A #GError.
*
* Creates a D-Bus error name to use for @error. If @error matches
* a registered error (cf. g_dbus_error_register_error()), the corresponding
* D-Bus error name will be returned.
*
* Otherwise the a name of the form
* <literal>org.gtk.GDBus.UnmappedGError.Quark._ESCAPED_QUARK_NAME.Code_ERROR_CODE</literal>
* will be used. This allows other GDBus applications to map the error
* on the wire back to a #GError using g_dbus_error_new_for_dbus_error().
*
* This function is typically only used in object mappings to put a
* #GError on the wire. Regular applications should not use it.
*
* Returns: A D-Bus error name (never %NULL). Free with g_free().
*/
gchar *
g_dbus_error_encode_gerror (const GError *error)
{
RegisteredError *re;
gchar *error_name;
g_return_val_if_fail (error != NULL, NULL);
/* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
_g_dbus_initialize ();
error_name = NULL;
G_LOCK (error_lock);
re = NULL;
if (quark_code_pair_to_re != NULL)
{
QuarkCodePair pair;
pair.error_domain = error->domain;
pair.error_code = error->code;
g_assert (dbus_error_name_to_re != NULL); /* check invariant */
re = g_hash_table_lookup (quark_code_pair_to_re, &pair);
}
if (re != NULL)
{
error_name = g_strdup (re->dbus_error_name);
G_UNLOCK (error_lock);
}
else
{
const gchar *domain_as_string;
GString *s;
guint n;
G_UNLOCK (error_lock);
/* We can't make a lot of assumptions about what domain_as_string
* looks like and D-Bus is extremely picky about error names so
* hex-encode it for transport across the wire.
*/
domain_as_string = g_quark_to_string (error->domain);
s = g_string_new ("org.gtk.GDBus.UnmappedGError.Quark._");
for (n = 0; domain_as_string[n] != 0; n++)
{
gint c = domain_as_string[n];
if (g_ascii_isalnum (c))
{
g_string_append_c (s, c);
}
else
{
guint nibble_top;
guint nibble_bottom;
g_string_append_c (s, '_');
nibble_top = ((int) domain_as_string[n]) >> 4;
nibble_bottom = ((int) domain_as_string[n]) & 0x0f;
if (nibble_top < 10)
nibble_top += '0';
else
nibble_top += 'a' - 10;
if (nibble_bottom < 10)
nibble_bottom += '0';
else
nibble_bottom += 'a' - 10;
g_string_append_c (s, nibble_top);
g_string_append_c (s, nibble_bottom);
}
}
g_string_append_printf (s, ".Code%d", error->code);
error_name = g_string_free (s, FALSE);
}
return error_name;
}

92
gio/gdbuserror.h Normal file
View File

@ -0,0 +1,92 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_ERROR_H__
#define __G_DBUS_ERROR_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
/**
* G_DBUS_ERROR:
*
* Error domain for errors generated by a remote message bus. Errors
* in this domain will be from the #GDBusError enumeration. See
* #GError for more information on error domains.
*
* Note that errors in this error domain is intended only for
* returning errors from a remote message bus process. Errors
* generated locally in-process by e.g. #GDBusConnection is from the
* %G_IO_ERROR domain.
*/
#define G_DBUS_ERROR g_dbus_error_quark()
GQuark g_dbus_error_quark (void);
/* Used by applications to check, get and strip the D-Bus error name */
gboolean g_dbus_error_is_remote_error (const GError *error);
gchar *g_dbus_error_get_remote_error (const GError *error);
gboolean g_dbus_error_strip_remote_error (GError *error);
/**
* GDBusErrorEntry:
* @error_code: An error code.
* @dbus_error_name: The D-Bus error name to associate with @error_code.
*
* Struct used in g_dbus_error_register_error_domain().
*/
struct _GDBusErrorEntry
{
gint error_code;
const gchar *dbus_error_name;
};
gboolean g_dbus_error_register_error (GQuark error_domain,
gint error_code,
const gchar *dbus_error_name);
gboolean g_dbus_error_unregister_error (GQuark error_domain,
gint error_code,
const gchar *dbus_error_name);
void g_dbus_error_register_error_domain (const gchar *error_domain_quark_name,
volatile gsize *quark_volatile,
const GDBusErrorEntry *entries,
guint num_entries);
/* Only used by object mappings to map back and forth to GError */
GError *g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name,
const gchar *dbus_error_message);
void g_dbus_error_set_dbus_error (GError **error,
const gchar *dbus_error_name,
const gchar *dbus_error_message,
const gchar *format,
...);
void g_dbus_error_set_dbus_error_valist (GError **error,
const gchar *dbus_error_name,
const gchar *dbus_error_message,
const gchar *format,
va_list var_args);
gchar *g_dbus_error_encode_gerror (const GError *error);
G_END_DECLS
#endif /* __G_DBUS_ERROR_H__ */

2009
gio/gdbusintrospection.c Normal file

File diff suppressed because it is too large Load Diff

255
gio/gdbusintrospection.h Normal file
View File

@ -0,0 +1,255 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_INTROSPECTION_H__
#define __G_DBUS_INTROSPECTION_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
/**
* GDBusAnnotationInfo:
* @ref_count: The reference count or -1 if statically allocated.
* @key: The name of the annotation, e.g. "org.freedesktop.DBus.Deprecated".
* @value: The value of the annotation.
* @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations.
*
* Information about an annotation.
*/
struct _GDBusAnnotationInfo
{
volatile gint ref_count;
gchar *key;
gchar *value;
GDBusAnnotationInfo **annotations;
};
/**
* GDBusArgInfo:
* @ref_count: The reference count or -1 if statically allocated.
* @name: Name of the argument, e.g. @unix_user_id.
* @signature: D-Bus signature of the argument (a single complete type).
* @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations.
*
* Information about an argument for a method or a signal.
*/
struct _GDBusArgInfo
{
volatile gint ref_count;
gchar *name;
gchar *signature;
GDBusAnnotationInfo **annotations;
};
/**
* GDBusMethodInfo:
* @ref_count: The reference count or -1 if statically allocated.
* @name: The name of the D-Bus method, e.g. @RequestName.
* @in_args: A pointer to a %NULL-terminated array of pointers to #GDBusArgInfo structures or %NULL if there are no in arguments.
* @out_args: A pointer to a %NULL-terminated array of pointers to #GDBusArgInfo structures or %NULL if there are no out arguments.
* @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations.
*
* Information about a method on an D-Bus interface.
*/
struct _GDBusMethodInfo
{
volatile gint ref_count;
gchar *name;
GDBusArgInfo **in_args;
GDBusArgInfo **out_args;
GDBusAnnotationInfo **annotations;
};
/**
* GDBusSignalInfo:
* @ref_count: The reference count or -1 if statically allocated.
* @name: The name of the D-Bus signal, e.g. "NameOwnerChanged".
* @args: A pointer to a %NULL-terminated array of pointers to #GDBusArgInfo structures or %NULL if there are no arguments.
* @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations.
*
* Information about a signal on a D-Bus interface.
*/
struct _GDBusSignalInfo
{
volatile gint ref_count;
gchar *name;
GDBusArgInfo **args;
GDBusAnnotationInfo **annotations;
};
/**
* GDBusPropertyInfo:
* @ref_count: The reference count or -1 if statically allocated.
* @name: The name of the D-Bus property, e.g. "SupportedFilesystems".
* @signature: The D-Bus signature of the property (a single complete type).
* @flags: Access control flags for the property.
* @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations.
*
* Information about a D-Bus property on a D-Bus interface.
*/
struct _GDBusPropertyInfo
{
volatile gint ref_count;
gchar *name;
gchar *signature;
GDBusPropertyInfoFlags flags;
GDBusAnnotationInfo **annotations;
};
/**
* GDBusInterfaceInfo:
* @ref_count: The reference count or -1 if statically allocated.
* @name: The name of the D-Bus interface, e.g. "org.freedesktop.DBus.Properties".
* @methods: A pointer to a %NULL-terminated array of pointers to #GDBusMethodInfo structures or %NULL if there are no methods.
* @signals: A pointer to a %NULL-terminated array of pointers to #GDBusSignalInfo structures or %NULL if there are no signals.
* @properties: A pointer to a %NULL-terminated array of pointers to #GDBusPropertyInfo structures or %NULL if there are no properties.
* @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations.
*
* Information about a D-Bus interface.
*/
struct _GDBusInterfaceInfo
{
volatile gint ref_count;
gchar *name;
GDBusMethodInfo **methods;
GDBusSignalInfo **signals;
GDBusPropertyInfo **properties;
GDBusAnnotationInfo **annotations;
};
/**
* GDBusNodeInfo:
* @ref_count: The reference count or -1 if statically allocated.
* @path: The path of the node or %NULL if omitted. Note that this may be a relative path. See the D-Bus specification for more details.
* @interfaces: A pointer to a %NULL-terminated array of pointers to #GDBusInterfaceInfo structures or %NULL if there are no interfaces.
* @nodes: A pointer to a %NULL-terminated array of pointers to #GDBusNodeInfo structures or %NULL if there are no nodes.
* @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations.
*
* Information about nodes in a remote object hierarchy.
*/
struct _GDBusNodeInfo
{
volatile gint ref_count;
gchar *path;
GDBusInterfaceInfo **interfaces;
GDBusNodeInfo **nodes;
GDBusAnnotationInfo **annotations;
};
const gchar *g_dbus_annotation_info_lookup (const GDBusAnnotationInfo **annotations,
const gchar *name);
const GDBusMethodInfo *g_dbus_interface_info_lookup_method (const GDBusInterfaceInfo *info,
const gchar *name);
const GDBusSignalInfo *g_dbus_interface_info_lookup_signal (const GDBusInterfaceInfo *info,
const gchar *name);
const GDBusPropertyInfo *g_dbus_interface_info_lookup_property (const GDBusInterfaceInfo *info,
const gchar *name);
void g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo *info,
guint indent,
GString *string_builder);
GDBusNodeInfo *g_dbus_node_info_new_for_xml (const gchar *xml_data,
GError **error);
const GDBusInterfaceInfo *g_dbus_node_info_lookup_interface (const GDBusNodeInfo *info,
const gchar *name);
void g_dbus_node_info_generate_xml (const GDBusNodeInfo *info,
guint indent,
GString *string_builder);
GDBusNodeInfo *g_dbus_node_info_ref (GDBusNodeInfo *info);
GDBusInterfaceInfo *g_dbus_interface_info_ref (GDBusInterfaceInfo *info);
GDBusMethodInfo *g_dbus_method_info_ref (GDBusMethodInfo *info);
GDBusSignalInfo *g_dbus_signal_info_ref (GDBusSignalInfo *info);
GDBusPropertyInfo *g_dbus_property_info_ref (GDBusPropertyInfo *info);
GDBusArgInfo *g_dbus_arg_info_ref (GDBusArgInfo *info);
GDBusAnnotationInfo *g_dbus_annotation_info_ref (GDBusAnnotationInfo *info);
void g_dbus_node_info_unref (GDBusNodeInfo *info);
void g_dbus_interface_info_unref (GDBusInterfaceInfo *info);
void g_dbus_method_info_unref (GDBusMethodInfo *info);
void g_dbus_signal_info_unref (GDBusSignalInfo *info);
void g_dbus_property_info_unref (GDBusPropertyInfo *info);
void g_dbus_arg_info_unref (GDBusArgInfo *info);
void g_dbus_annotation_info_unref (GDBusAnnotationInfo *info);
/**
* G_TYPE_DBUS_NODE_INFO:
*
* The #GType for a boxed type holding a #GDBusNodeInfo.
*/
#define G_TYPE_DBUS_NODE_INFO (g_dbus_node_info_get_type ())
/**
* G_TYPE_DBUS_INTERFACE_INFO:
*
* The #GType for a boxed type holding a #GDBusInterfaceInfo.
*/
#define G_TYPE_DBUS_INTERFACE_INFO (g_dbus_interface_info_get_type ())
/**
* G_TYPE_DBUS_METHOD_INFO:
*
* The #GType for a boxed type holding a #GDBusMethodInfo.
*/
#define G_TYPE_DBUS_METHOD_INFO (g_dbus_method_info_get_type ())
/**
* G_TYPE_DBUS_SIGNAL_INFO:
*
* The #GType for a boxed type holding a #GDBusSignalInfo.
*/
#define G_TYPE_DBUS_SIGNAL_INFO (g_dbus_signal_info_get_type ())
/**
* G_TYPE_DBUS_PROPERTY_INFO:
*
* The #GType for a boxed type holding a #GDBusPropertyInfo.
*/
#define G_TYPE_DBUS_PROPERTY_INFO (g_dbus_property_info_get_type ())
/**
* G_TYPE_DBUS_ARG_INFO:
*
* The #GType for a boxed type holding a #GDBusArgInfo.
*/
#define G_TYPE_DBUS_ARG_INFO (g_dbus_arg_info_get_type ())
/**
* G_TYPE_DBUS_ANNOTATION_INFO:
*
* The #GType for a boxed type holding a #GDBusAnnotationInfo.
*/
#define G_TYPE_DBUS_ANNOTATION_INFO (g_dbus_annotation_info_get_type ())
GType g_dbus_node_info_get_type (void) G_GNUC_CONST;
GType g_dbus_interface_info_get_type (void) G_GNUC_CONST;
GType g_dbus_method_info_get_type (void) G_GNUC_CONST;
GType g_dbus_signal_info_get_type (void) G_GNUC_CONST;
GType g_dbus_property_info_get_type (void) G_GNUC_CONST;
GType g_dbus_arg_info_get_type (void) G_GNUC_CONST;
GType g_dbus_annotation_info_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_DBUS_INTROSPECTION_H__ */

2421
gio/gdbusmessage.c Normal file

File diff suppressed because it is too large Load Diff

172
gio/gdbusmessage.h Normal file
View File

@ -0,0 +1,172 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_MESSAGE_H__
#define __G_DBUS_MESSAGE_H__
#include <gio/giotypes.h>
#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
#endif
G_BEGIN_DECLS
#define G_TYPE_DBUS_MESSAGE (g_dbus_message_get_gtype ())
#define G_DBUS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_MESSAGE, GDBusMessage))
#define G_DBUS_MESSAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_MESSAGE, GDBusMessageClass))
#define G_DBUS_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_MESSAGE, GDBusMessageClass))
#define G_IS_DBUS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_MESSAGE))
#define G_IS_DBUS_MESSAGE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_MESSAGE))
typedef struct _GDBusMessageClass GDBusMessageClass;
typedef struct _GDBusMessagePrivate GDBusMessagePrivate;
/**
* GDBusMessageClass:
*
* Class structure for #GDBusMessage.
*/
struct _GDBusMessageClass
{
/*< private >*/
GObjectClass parent_class;
};
/**
* GDBusMessage:
*
* The #GDBusMessage structure contains only private data and should
* only be accessed using the provided API.
*/
struct _GDBusMessage
{
/*< private >*/
GObject parent_instance;
GDBusMessagePrivate *priv;
};
GType g_dbus_message_get_gtype (void) G_GNUC_CONST;
GDBusMessage *g_dbus_message_new (void);
GDBusMessage *g_dbus_message_new_signal (const gchar *path,
const gchar *interface,
const gchar *signal);
GDBusMessage *g_dbus_message_new_method_call (const gchar *name,
const gchar *path,
const gchar *interface,
const gchar *method);
GDBusMessage *g_dbus_message_new_method_reply (GDBusMessage *method_call_message);
GDBusMessage *g_dbus_message_new_method_error (GDBusMessage *method_call_message,
const gchar *error_name,
const gchar *error_message_format,
...);
GDBusMessage *g_dbus_message_new_method_error_valist (GDBusMessage *method_call_message,
const gchar *error_name,
const gchar *error_message_format,
va_list var_args);
GDBusMessage *g_dbus_message_new_method_error_literal (GDBusMessage *method_call_message,
const gchar *error_name,
const gchar *error_message);
gchar *g_dbus_message_print (GDBusMessage *message,
guint indent);
GDBusMessageType g_dbus_message_get_type (GDBusMessage *message);
void g_dbus_message_set_type (GDBusMessage *message,
GDBusMessageType type);
GDBusMessageFlags g_dbus_message_get_flags (GDBusMessage *message);
void g_dbus_message_set_flags (GDBusMessage *message,
GDBusMessageFlags flags);
guint32 g_dbus_message_get_serial (GDBusMessage *message);
void g_dbus_message_set_serial (GDBusMessage *message,
guint32 serial);
GVariant *g_dbus_message_get_header (GDBusMessage *message,
GDBusMessageHeaderField header_field);
void g_dbus_message_set_header (GDBusMessage *message,
GDBusMessageHeaderField header_field,
GVariant *value);
guchar *g_dbus_message_get_header_fields (GDBusMessage *message);
GVariant *g_dbus_message_get_body (GDBusMessage *message);
void g_dbus_message_set_body (GDBusMessage *message,
GVariant *body);
#ifdef G_OS_UNIX
GUnixFDList *g_dbus_message_get_unix_fd_list (GDBusMessage *message);
void g_dbus_message_set_unix_fd_list (GDBusMessage *message,
GUnixFDList *fd_list);
#endif
guint32 g_dbus_message_get_reply_serial (GDBusMessage *message);
void g_dbus_message_set_reply_serial (GDBusMessage *message,
guint32 value);
const gchar *g_dbus_message_get_interface (GDBusMessage *message);
void g_dbus_message_set_interface (GDBusMessage *message,
const gchar *value);
const gchar *g_dbus_message_get_member (GDBusMessage *message);
void g_dbus_message_set_member (GDBusMessage *message,
const gchar *value);
const gchar *g_dbus_message_get_path (GDBusMessage *message);
void g_dbus_message_set_path (GDBusMessage *message,
const gchar *value);
const gchar *g_dbus_message_get_sender (GDBusMessage *message);
void g_dbus_message_set_sender (GDBusMessage *message,
const gchar *value);
const gchar *g_dbus_message_get_destination (GDBusMessage *message);
void g_dbus_message_set_destination (GDBusMessage *message,
const gchar *value);
const gchar *g_dbus_message_get_error_name (GDBusMessage *message);
void g_dbus_message_set_error_name (GDBusMessage *message,
const gchar *value);
const gchar *g_dbus_message_get_signature (GDBusMessage *message);
void g_dbus_message_set_signature (GDBusMessage *message,
const gchar *value);
guint32 g_dbus_message_get_num_unix_fds (GDBusMessage *message);
void g_dbus_message_set_num_unix_fds (GDBusMessage *message,
guint32 value);
const gchar *g_dbus_message_get_arg0 (GDBusMessage *message);
GDBusMessage *g_dbus_message_new_from_blob (guchar *blob,
gsize blob_len,
GError **error);
gssize g_dbus_message_bytes_needed (guchar *blob,
gsize blob_len,
GError **error);
guchar *g_dbus_message_to_blob (GDBusMessage *message,
gsize *out_size,
GError **error);
gboolean g_dbus_message_to_gerror (GDBusMessage *message,
GError **error);
G_END_DECLS
#endif /* __G_DBUS_MESSAGE_H__ */

795
gio/gdbusmethodinvocation.c Normal file
View File

@ -0,0 +1,795 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <glib/gi18n.h>
#include "gdbusutils.h"
#include "gdbusconnection.h"
#include "gdbusmessage.h"
#include "gdbusmethodinvocation.h"
#include "gdbusintrospection.h"
#include "gdbuserror.h"
#include "gdbusprivate.h"
/**
* SECTION:gdbusmethodinvocation
* @short_description: Object for handling remote calls
* @include: gdbus/gdbus.h
*
* Instances of the #GDBusMethodInvocation class are used when
* handling D-Bus method calls. It provides a way to asynchronously
* return results and errors.
*/
struct _GDBusMethodInvocationPrivate
{
/* construct-only properties */
gchar *sender;
gchar *object_path;
gchar *interface_name;
gchar *method_name;
const GDBusMethodInfo *method_info;
GDBusConnection *connection;
GDBusMessage *message;
GVariant *parameters;
gpointer user_data;
};
enum
{
PROP_0,
PROP_SENDER,
PROP_OBJECT_PATH,
PROP_INTERFACE_NAME,
PROP_METHOD_NAME,
PROP_METHOD_INFO,
PROP_CONNECTION,
PROP_PARAMETERS,
PROP_MESSAGE,
PROP_USER_DATA
};
G_DEFINE_TYPE (GDBusMethodInvocation, g_dbus_method_invocation, G_TYPE_OBJECT);
static void
g_dbus_method_invocation_finalize (GObject *object)
{
GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (object);
g_free (invocation->priv->sender);
g_free (invocation->priv->object_path);
g_free (invocation->priv->interface_name);
g_free (invocation->priv->method_name);
g_object_unref (invocation->priv->connection);
g_object_unref (invocation->priv->message);
g_variant_unref (invocation->priv->parameters);
if (G_OBJECT_CLASS (g_dbus_method_invocation_parent_class)->finalize != NULL)
G_OBJECT_CLASS (g_dbus_method_invocation_parent_class)->finalize (object);
}
static void
g_dbus_method_invocation_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (object);
switch (prop_id)
{
case PROP_SENDER:
g_value_set_string (value, g_dbus_method_invocation_get_sender (invocation));
break;
case PROP_OBJECT_PATH:
g_value_set_string (value, g_dbus_method_invocation_get_object_path (invocation));
break;
case PROP_INTERFACE_NAME:
g_value_set_string (value, g_dbus_method_invocation_get_interface_name (invocation));
break;
case PROP_METHOD_NAME:
g_value_set_string (value, g_dbus_method_invocation_get_method_name (invocation));
break;
case PROP_METHOD_INFO:
g_value_set_boxed (value, g_dbus_method_invocation_get_method_info (invocation));
break;
case PROP_CONNECTION:
g_value_set_object (value, g_dbus_method_invocation_get_connection (invocation));
break;
case PROP_PARAMETERS:
g_value_set_boxed (value, g_dbus_method_invocation_get_parameters (invocation));
break;
case PROP_MESSAGE:
g_value_set_object (value, g_dbus_method_invocation_get_message (invocation));
break;
case PROP_USER_DATA:
g_value_set_pointer (value, g_dbus_method_invocation_get_user_data (invocation));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_dbus_method_invocation_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (object);
switch (prop_id)
{
case PROP_SENDER:
invocation->priv->sender = g_value_dup_string (value);
break;
case PROP_OBJECT_PATH:
invocation->priv->object_path = g_value_dup_string (value);
break;
case PROP_INTERFACE_NAME:
invocation->priv->interface_name = g_value_dup_string (value);
break;
case PROP_METHOD_NAME:
invocation->priv->method_name = g_value_dup_string (value);
break;
case PROP_METHOD_INFO:
invocation->priv->method_info = g_value_dup_boxed (value);
break;
case PROP_CONNECTION:
invocation->priv->connection = g_value_dup_object (value);
break;
case PROP_PARAMETERS:
invocation->priv->parameters = g_value_dup_boxed (value);
break;
case PROP_MESSAGE:
invocation->priv->message = g_value_dup_object (value);
break;
case PROP_USER_DATA:
invocation->priv->user_data = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_dbus_method_invocation_finalize;
gobject_class->set_property = g_dbus_method_invocation_set_property;
gobject_class->get_property = g_dbus_method_invocation_get_property;
/**
* GDBusMethodInvocation:sender:
*
* The bus name that invoked the method or %NULL if the connection is not a bus connection.
*/
g_object_class_install_property (gobject_class,
PROP_SENDER,
g_param_spec_string ("sender",
_("Sender"),
_("The bus name that invoked the method."),
NULL,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusMethodInvocation:object-path:
*
* The object path the method was invoked on.
*/
g_object_class_install_property (gobject_class,
PROP_OBJECT_PATH,
g_param_spec_string ("object-path",
_("Object Path"),
_("The object path the method was invoked on."),
NULL,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusMethodInvocation:interface-name:
*
* The name of the D-Bus interface the method was invoked on.
*/
g_object_class_install_property (gobject_class,
PROP_INTERFACE_NAME,
g_param_spec_string ("interface-name",
_("Interface Name"),
_("The name of the D-Bus interface the method was invoked on."),
NULL,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusMethodInvocation:method-name:
*
* The name of the method that was invoked.
*/
g_object_class_install_property (gobject_class,
PROP_METHOD_NAME,
g_param_spec_string ("method-name",
_("Method Name"),
_("The name of the method that was invoked."),
NULL,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusMethodInvocation:method-info:
*
* Information about the method that was invoked, if any.
*/
g_object_class_install_property (gobject_class,
PROP_METHOD_INFO,
g_param_spec_boxed ("method-info",
_("Method Info"),
_("Information about the method that was invoked, if any."),
G_TYPE_DBUS_METHOD_INFO,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusMethodInvocation:connection:
*
* The #GDBusConnection the method was invoked on.
*/
g_object_class_install_property (gobject_class,
PROP_CONNECTION,
g_param_spec_object ("connection",
_("Connection"),
_("The #GDBusConnection the method was invoked on."),
G_TYPE_DBUS_CONNECTION,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusMethodInvocation:message:
*
* The D-Bus message.
*/
g_object_class_install_property (gobject_class,
PROP_MESSAGE,
g_param_spec_object ("message",
_("Message"),
_("The D-Bus Message."),
G_TYPE_DBUS_MESSAGE,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusMethodInvocation:parameters:
*
* The parameters as a #GVariant tuple.
*/
g_object_class_install_property (gobject_class,
PROP_PARAMETERS,
g_param_spec_boxed ("parameters",
_("Parameters"),
_("The parameters as a #GVariant tuple."),
G_TYPE_VARIANT,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusMethodInvocation:user-data:
*
* The @user_data #gpointer passed to g_dbus_connection_register_object().
*/
g_object_class_install_property (gobject_class,
PROP_USER_DATA,
g_param_spec_pointer ("user-data",
_("User Data"),
_("The gpointer passed to g_dbus_connection_register_object()."),
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
g_type_class_add_private (klass, sizeof (GDBusMethodInvocationPrivate));
}
static void
g_dbus_method_invocation_init (GDBusMethodInvocation *invocation)
{
invocation->priv = G_TYPE_INSTANCE_GET_PRIVATE (invocation,
G_TYPE_DBUS_METHOD_INVOCATION,
GDBusMethodInvocationPrivate);
}
/**
* g_dbus_method_invocation_get_sender:
* @invocation: A #GDBusMethodInvocation.
*
* Gets the bus name that invoked the method.
*
* Returns: A string. Do not free, it is owned by @invocation.
*/
const gchar *
g_dbus_method_invocation_get_sender (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->sender;
}
/**
* g_dbus_method_invocation_get_object_path:
* @invocation: A #GDBusMethodInvocation.
*
* Gets the object path the method was invoked on.
*
* Returns: A string. Do not free, it is owned by @invocation.
*/
const gchar *
g_dbus_method_invocation_get_object_path (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->object_path;
}
/**
* g_dbus_method_invocation_get_interface_name:
* @invocation: A #GDBusMethodInvocation.
*
* Gets the name of the D-Bus interface the method was invoked on.
*
* Returns: A string. Do not free, it is owned by @invocation.
*/
const gchar *
g_dbus_method_invocation_get_interface_name (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->interface_name;
}
/**
* g_dbus_method_invocation_get_method_info:
* @invocation: A #GDBusMethodInvocation.
*
* Gets information about the method call, if any.
*
* Returns: A #GDBusMethodInfo or %NULL. Do not free, it is owned by @invocation.
*/
const GDBusMethodInfo *
g_dbus_method_invocation_get_method_info (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->method_info;
}
/**
* g_dbus_method_invocation_get_method_name:
* @invocation: A #GDBusMethodInvocation.
*
* Gets the name of the method that was invoked.
*
* Returns: A string. Do not free, it is owned by @invocation.
*/
const gchar *
g_dbus_method_invocation_get_method_name (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->method_name;
}
/**
* g_dbus_method_invocation_get_connection:
* @invocation: A #GDBusMethodInvocation.
*
* Gets the #GDBusConnection the method was invoked on.
*
* Returns: A #GDBusConnection. Do not free, it is owned by @invocation.
*/
GDBusConnection *
g_dbus_method_invocation_get_connection (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->connection;
}
/**
* g_dbus_method_invocation_get_message:
* @invocation: A #GDBusMethodInvocation.
*
* Gets the #GDBusMessage for the method invocation. This is useful if
* you need to use low-level protocol features, such as UNIX file
* descriptor passing, that cannot be properly expressed in the
* #GVariant API.
*
* See <xref linkend="gdbus-server"/> and <xref
* linkend="gdbus-unix-fd-client"/> for an example of how to use this
* low-level API to send and receive UNIX file descriptors.
*
* Returns: A #GDBusMessage. Do not free, it is owned by @invocation.
*/
GDBusMessage *
g_dbus_method_invocation_get_message (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->message;
}
/**
* g_dbus_method_invocation_get_parameters:
* @invocation: A #GDBusMethodInvocation.
*
* Gets the parameters of the method invocation.
*
* Returns: A #GVariant. Do not free, it is owned by @invocation.
*/
GVariant *
g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->parameters;
}
/**
* g_dbus_method_invocation_get_user_data:
* @invocation: A #GDBusMethodInvocation.
*
* Gets the @user_data #gpointer passed to g_dbus_connection_register_object().
*
* Returns: A #gpointer.
*/
gpointer
g_dbus_method_invocation_get_user_data (GDBusMethodInvocation *invocation)
{
g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
return invocation->priv->user_data;
}
/**
* g_dbus_method_invocation_new:
* @sender: The bus name that invoked the method or %NULL if @connection is not a bus connection.
* @object_path: The object path the method was invoked on.
* @interface_name: The name of the D-Bus interface the method was invoked on.
* @method_name: The name of the method that was invoked.
* @method_info: Information about the method call or %NULL.
* @connection: The #GDBusConnection the method was invoked on.
* @message: The D-Bus message as a #GDBusMessage.
* @parameters: The parameters as a #GVariant tuple.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
*
* Creates a new #GDBusMethodInvocation object.
*
* Returns: A #GDBusMethodInvocation. Free with g_object_unref().
*/
GDBusMethodInvocation *
g_dbus_method_invocation_new (const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
const GDBusMethodInfo *method_info,
GDBusConnection *connection,
GDBusMessage *message,
GVariant *parameters,
gpointer user_data)
{
g_return_val_if_fail (sender == NULL || g_dbus_is_name (sender), NULL);
g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), NULL);
g_return_val_if_fail (g_dbus_is_member_name (method_name), NULL);
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
g_return_val_if_fail (g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL);
return G_DBUS_METHOD_INVOCATION (g_object_new (G_TYPE_DBUS_METHOD_INVOCATION,
"sender", sender,
"object-path", object_path,
"interface-name", interface_name,
"method-name", method_name,
"method-info", method_info,
"connection", connection,
"message", message,
"parameters", parameters,
"user-data", user_data,
NULL));
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_dbus_method_invocation_return_value:
* @invocation: A #GDBusMethodInvocation.
* @parameters: A #GVariant tuple with out parameters for the method or %NULL if not passing any parameters.
*
* Finishes handling a D-Bus method call by returning @parameters.
*
* It is an error if @parameters is not of the right format.
*
* This method will free @invocation, you cannot use it afterwards.
*/
void
g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation,
GVariant *parameters)
{
GDBusMessage *reply;
GError *error;
g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
if (parameters != NULL)
g_variant_ref_sink (parameters);
/* if we have introspection data, check that the signature of @parameters is correct */
if (invocation->priv->method_info != NULL)
{
gchar *signature;
const gchar *type_string;
type_string = "()";
if (parameters != NULL)
type_string = g_variant_get_type_string (parameters);
signature = _g_dbus_compute_complete_signature (invocation->priv->method_info->out_args, TRUE);
if (g_strcmp0 (type_string, signature) != 0)
{
g_warning (_("Type of return value is incorrect, got `%s', expected `%s'"),
type_string,
signature);
g_free (signature);
goto out;
}
g_free (signature);
}
reply = g_dbus_message_new_method_reply (invocation->priv->message);
g_dbus_message_set_body (reply, parameters);
error = NULL;
if (!g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, NULL, &error))
{
g_warning (_("Error sending message: %s"), error->message);
g_error_free (error);
}
g_object_unref (reply);
out:
g_object_unref (invocation);
if (parameters != NULL)
g_variant_unref (parameters);
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_dbus_method_invocation_return_error:
* @invocation: A #GDBusMethodInvocation.
* @domain: A #GQuark for the #GError error domain.
* @code: The error code.
* @format: printf()-style format.
* @...: Parameters for @format.
*
* Finishes handling a D-Bus method call by returning an error.
*
* See g_dbus_error_encode_gerror() for details about what error name
* will be returned on the wire. In a nutshell, if the given error is
* registered using g_dbus_error_register_error() the name given
* during registration is used. Otherwise, a name of the form
* <literal>org.gtk.GDBus.UnmappedGError.Quark...</literal> is
* used. This provides transparent mapping of #GError between
* applications using GDBus.
*
* If you are writing an application intended to be portable,
* <emphasis>always</emphasis> register errors with g_dbus_error_register_error()
* or use g_dbus_method_invocation_return_dbus_error().
*
* This method will free @invocation, you cannot use it afterwards.
*/
void
g_dbus_method_invocation_return_error (GDBusMethodInvocation *invocation,
GQuark domain,
gint code,
const gchar *format,
...)
{
va_list var_args;
g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
g_return_if_fail (format != NULL);
va_start (var_args, format);
g_dbus_method_invocation_return_error_valist (invocation,
domain,
code,
format,
var_args);
va_end (var_args);
}
/**
* g_dbus_method_invocation_return_error_valist:
* @invocation: A #GDBusMethodInvocation.
* @domain: A #GQuark for the #GError error domain.
* @code: The error code.
* @format: printf()-style format.
* @var_args: #va_list of parameters for @format.
*
* Like g_dbus_method_invocation_return_error() but intended for
* language bindings.
*
* This method will free @invocation, you cannot use it afterwards.
*/
void
g_dbus_method_invocation_return_error_valist (GDBusMethodInvocation *invocation,
GQuark domain,
gint code,
const gchar *format,
va_list var_args)
{
gchar *literal_message;
g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
g_return_if_fail (format != NULL);
literal_message = g_strdup_vprintf (format, var_args);
g_dbus_method_invocation_return_error_literal (invocation,
domain,
code,
literal_message);
g_free (literal_message);
}
/**
* g_dbus_method_invocation_return_error_literal:
* @invocation: A #GDBusMethodInvocation.
* @domain: A #GQuark for the #GError error domain.
* @code: The error code.
* @message: The error message.
*
* Like g_dbus_method_invocation_return_error() but without printf()-style formatting.
*
* This method will free @invocation, you cannot use it afterwards.
*/
void
g_dbus_method_invocation_return_error_literal (GDBusMethodInvocation *invocation,
GQuark domain,
gint code,
const gchar *message)
{
GError *error;
g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
g_return_if_fail (message != NULL);
error = g_error_new_literal (domain, code, message);
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error);
}
/**
* g_dbus_method_invocation_return_gerror:
* @invocation: A #GDBusMethodInvocation.
* @error: A #GError.
*
* Like g_dbus_method_invocation_return_error() but takes a #GError
* instead of the error domain, error code and message.
*
* This method will free @invocation, you cannot use it afterwards.
*/
void
g_dbus_method_invocation_return_gerror (GDBusMethodInvocation *invocation,
const GError *error)
{
gchar *dbus_error_name;
g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
g_return_if_fail (error != NULL);
dbus_error_name = g_dbus_error_encode_gerror (error);
g_dbus_method_invocation_return_dbus_error (invocation,
dbus_error_name,
error->message);
g_free (dbus_error_name);
}
/**
* g_dbus_method_invocation_return_dbus_error:
* @invocation: A #GDBusMethodInvocation.
* @error_name: A valid D-Bus error name.
* @error_message: A valid D-Bus error message.
*
* Finishes handling a D-Bus method call by returning an error.
*
* This method will free @invocation, you cannot use it afterwards.
*/
void
g_dbus_method_invocation_return_dbus_error (GDBusMethodInvocation *invocation,
const gchar *error_name,
const gchar *error_message)
{
GDBusMessage *reply;
g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
g_return_if_fail (error_name != NULL && g_dbus_is_name (error_name));
g_return_if_fail (error_message != NULL);
reply = g_dbus_message_new_method_error_literal (invocation->priv->message,
error_name,
error_message);
g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, NULL, NULL);
g_object_unref (reply);
g_object_unref (invocation);
}

119
gio/gdbusmethodinvocation.h Normal file
View File

@ -0,0 +1,119 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_METHOD_INVOCATION_H__
#define __G_DBUS_METHOD_INVOCATION_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_METHOD_INVOCATION (g_dbus_method_invocation_get_type ())
#define G_DBUS_METHOD_INVOCATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_METHOD_INVOCATION, GDBusMethodInvocation))
#define G_DBUS_METHOD_INVOCATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_METHOD_INVOCATION, GDBusMethodInvocationClass))
#define G_DBUS_METHOD_INVOCATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_METHOD_INVOCATION, GDBusMethodInvocationClass))
#define G_IS_DBUS_METHOD_INVOCATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_METHOD_INVOCATION))
#define G_IS_DBUS_METHOD_INVOCATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_METHOD_INVOCATION))
typedef struct _GDBusMethodInvocationClass GDBusMethodInvocationClass;
typedef struct _GDBusMethodInvocationPrivate GDBusMethodInvocationPrivate;
/**
* GDBusMethodInvocation:
*
* The #GDBusMethodInvocation structure contains only private data and
* should only be accessed using the provided API.
*/
struct _GDBusMethodInvocation
{
/*< private >*/
GObject parent_instance;
GDBusMethodInvocationPrivate *priv;
};
/**
* GDBusMethodInvocationClass:
*
* Class structure for #GDBusMethodInvocation.
*/
struct _GDBusMethodInvocationClass
{
/*< private >*/
GObjectClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
};
GType g_dbus_method_invocation_get_type (void) G_GNUC_CONST;
GDBusMethodInvocation *g_dbus_method_invocation_new (const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
const GDBusMethodInfo *method_info,
GDBusConnection *connection,
GDBusMessage *message,
GVariant *parameters,
gpointer user_data);
const gchar *g_dbus_method_invocation_get_sender (GDBusMethodInvocation *invocation);
const gchar *g_dbus_method_invocation_get_object_path (GDBusMethodInvocation *invocation);
const gchar *g_dbus_method_invocation_get_interface_name (GDBusMethodInvocation *invocation);
const gchar *g_dbus_method_invocation_get_method_name (GDBusMethodInvocation *invocation);
const GDBusMethodInfo *g_dbus_method_invocation_get_method_info (GDBusMethodInvocation *invocation);
GDBusConnection *g_dbus_method_invocation_get_connection (GDBusMethodInvocation *invocation);
GDBusMessage *g_dbus_method_invocation_get_message (GDBusMethodInvocation *invocation);
GVariant *g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation);
gpointer g_dbus_method_invocation_get_user_data (GDBusMethodInvocation *invocation);
void g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation,
GVariant *parameters);
void g_dbus_method_invocation_return_error (GDBusMethodInvocation *invocation,
GQuark domain,
gint code,
const gchar *format,
...);
void g_dbus_method_invocation_return_error_valist (GDBusMethodInvocation *invocation,
GQuark domain,
gint code,
const gchar *format,
va_list var_args);
void g_dbus_method_invocation_return_error_literal (GDBusMethodInvocation *invocation,
GQuark domain,
gint code,
const gchar *message);
void g_dbus_method_invocation_return_gerror (GDBusMethodInvocation *invocation,
const GError *error);
void g_dbus_method_invocation_return_dbus_error (GDBusMethodInvocation *invocation,
const gchar *error_name,
const gchar *error_message);
G_END_DECLS
#endif /* __G_DBUS_METHOD_INVOCATION_H__ */

713
gio/gdbusnameowning.c Normal file
View File

@ -0,0 +1,713 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <glib/gi18n.h>
#include "gdbusutils.h"
#include "gdbusnameowning.h"
#include "gdbuserror.h"
#include "gdbusprivate.h"
#include "gdbusconnection.h"
/**
* SECTION:gdbusnameowning
* @title: Owning Bus Names
* @short_description: Simple API for owning bus names
* @include: gdbus/gdbus.h
*
* Convenience API for owning bus names.
*
* <example id="gdbus-owning-names"><title>Simple application owning a name</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../../gio/tests/gdbus-example-own-name.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
*/
G_LOCK_DEFINE_STATIC (lock);
/* ---------------------------------------------------------------------------------------------------- */
typedef enum
{
PREVIOUS_CALL_NONE = 0,
PREVIOUS_CALL_ACQUIRED,
PREVIOUS_CALL_LOST,
} PreviousCall;
typedef struct
{
volatile gint ref_count;
guint id;
GBusNameOwnerFlags flags;
gchar *name;
GBusAcquiredCallback bus_acquired_handler;
GBusNameAcquiredCallback name_acquired_handler;
GBusNameLostCallback name_lost_handler;
gpointer user_data;
GDestroyNotify user_data_free_func;
GMainContext *main_context;
PreviousCall previous_call;
GDBusConnection *connection;
gulong disconnected_signal_handler_id;
guint name_acquired_subscription_id;
guint name_lost_subscription_id;
gboolean cancelled;
gboolean needs_release;
} Client;
static guint next_global_id = 1;
static GHashTable *map_id_to_client = NULL;
static Client *
client_ref (Client *client)
{
g_atomic_int_inc (&client->ref_count);
return client;
}
static void
client_unref (Client *client)
{
if (g_atomic_int_dec_and_test (&client->ref_count))
{
if (client->connection != NULL)
{
if (client->disconnected_signal_handler_id > 0)
g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
if (client->name_acquired_subscription_id > 0)
g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id);
if (client->name_lost_subscription_id > 0)
g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id);
g_object_unref (client->connection);
}
if (client->main_context != NULL)
g_main_context_unref (client->main_context);
g_free (client->name);
if (client->user_data_free_func != NULL)
client->user_data_free_func (client->user_data);
g_free (client);
}
}
/* ---------------------------------------------------------------------------------------------------- */
typedef enum
{
CALL_TYPE_NAME_ACQUIRED,
CALL_TYPE_NAME_LOST
} CallType;
typedef struct
{
Client *client;
/* keep this separate because client->connection may
* be set to NULL after scheduling the call
*/
GDBusConnection *connection;
/* set to TRUE to call acquired */
CallType call_type;
} CallHandlerData;
static void
call_handler_data_free (CallHandlerData *data)
{
if (data->connection != NULL)
g_object_unref (data->connection);
client_unref (data->client);
g_free (data);
}
static void
actually_do_call (Client *client, GDBusConnection *connection, CallType call_type)
{
switch (call_type)
{
case CALL_TYPE_NAME_ACQUIRED:
if (client->name_acquired_handler != NULL)
{
client->name_acquired_handler (connection,
client->name,
client->user_data);
}
break;
case CALL_TYPE_NAME_LOST:
if (client->name_lost_handler != NULL)
{
client->name_lost_handler (connection,
client->name,
client->user_data);
}
break;
default:
g_assert_not_reached ();
break;
}
}
static gboolean
call_in_idle_cb (gpointer _data)
{
CallHandlerData *data = _data;
actually_do_call (data->client, data->connection, data->call_type);
return FALSE;
}
static void
schedule_call_in_idle (Client *client, CallType call_type)
{
CallHandlerData *data;
GSource *idle_source;
data = g_new0 (CallHandlerData, 1);
data->client = client_ref (client);
data->connection = client->connection != NULL ? g_object_ref (client->connection) : NULL;
data->call_type = call_type;
idle_source = g_idle_source_new ();
g_source_set_priority (idle_source, G_PRIORITY_HIGH);
g_source_set_callback (idle_source,
call_in_idle_cb,
data,
(GDestroyNotify) call_handler_data_free);
g_source_attach (idle_source, client->main_context);
g_source_unref (idle_source);
}
static void
do_call (Client *client, CallType call_type)
{
/* only schedule in idle if we're not in the right thread */
if (g_main_context_get_thread_default () != client->main_context)
schedule_call_in_idle (client, call_type);
else
actually_do_call (client, client->connection, call_type);
}
static void
call_acquired_handler (Client *client)
{
if (client->previous_call != PREVIOUS_CALL_ACQUIRED)
{
client->previous_call = PREVIOUS_CALL_ACQUIRED;
if (!client->cancelled)
{
do_call (client, CALL_TYPE_NAME_ACQUIRED);
}
}
}
static void
call_lost_handler (Client *client)
{
if (client->previous_call != PREVIOUS_CALL_LOST)
{
client->previous_call = PREVIOUS_CALL_LOST;
if (!client->cancelled)
{
do_call (client, CALL_TYPE_NAME_LOST);
}
}
}
/* ---------------------------------------------------------------------------------------------------- */
static void
on_name_lost_or_acquired (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
Client *client = user_data;
const gchar *name;
if (g_strcmp0 (object_path, "/org/freedesktop/DBus") != 0 ||
g_strcmp0 (interface_name, "org.freedesktop.DBus") != 0 ||
g_strcmp0 (sender_name, "org.freedesktop.DBus") != 0)
goto out;
if (g_strcmp0 (signal_name, "NameLost") == 0)
{
g_variant_get (parameters, "(s)", &name);
if (g_strcmp0 (name, client->name) == 0)
{
call_lost_handler (client);
}
}
else if (g_strcmp0 (signal_name, "NameAcquired") == 0)
{
g_variant_get (parameters, "(s)", &name);
if (g_strcmp0 (name, client->name) == 0)
{
call_acquired_handler (client);
}
}
out:
;
}
/* ---------------------------------------------------------------------------------------------------- */
static void
request_name_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
Client *client = user_data;
GVariant *result;
guint32 request_name_reply;
gboolean subscribe;
request_name_reply = 0;
result = NULL;
result = g_dbus_connection_invoke_method_finish (client->connection,
res,
NULL);
if (result != NULL)
{
g_variant_get (result, "(u)", &request_name_reply);
g_variant_unref (result);
}
subscribe = FALSE;
switch (request_name_reply)
{
case 1: /* DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER */
/* We got the name - now listen for NameLost and NameAcquired */
call_acquired_handler (client);
subscribe = TRUE;
client->needs_release = TRUE;
break;
case 2: /* DBUS_REQUEST_NAME_REPLY_IN_QUEUE */
/* Waiting in line - listen for NameLost and NameAcquired */
call_lost_handler (client);
subscribe = TRUE;
client->needs_release = TRUE;
break;
default:
/* assume we couldn't get the name - explicit fallthrough */
case 3: /* DBUS_REQUEST_NAME_REPLY_EXISTS */
case 4: /* DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER */
/* Some other part of the process is already owning the name */
call_lost_handler (client);
break;
}
if (subscribe)
{
/* start listening to NameLost and NameAcquired messages */
client->name_lost_subscription_id =
g_dbus_connection_signal_subscribe (client->connection,
"org.freedesktop.DBus",
"org.freedesktop.DBus",
"NameLost",
"/org/freedesktop/DBus",
client->name,
on_name_lost_or_acquired,
client,
NULL);
client->name_acquired_subscription_id =
g_dbus_connection_signal_subscribe (client->connection,
"org.freedesktop.DBus",
"org.freedesktop.DBus",
"NameAcquired",
"/org/freedesktop/DBus",
client->name,
on_name_lost_or_acquired,
client,
NULL);
}
client_unref (client);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
on_connection_disconnected (GDBusConnection *connection,
gboolean remote_peer_vanished,
GError *error,
gpointer user_data)
{
Client *client = user_data;
if (client->disconnected_signal_handler_id > 0)
g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
if (client->name_acquired_subscription_id > 0)
g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id);
if (client->name_lost_subscription_id > 0)
g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id);
g_object_unref (client->connection);
client->disconnected_signal_handler_id = 0;
client->name_acquired_subscription_id = 0;
client->name_lost_subscription_id = 0;
client->connection = NULL;
call_lost_handler (client);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
has_connection (Client *client)
{
/* listen for disconnection */
client->disconnected_signal_handler_id = g_signal_connect (client->connection,
"closed",
G_CALLBACK (on_connection_disconnected),
client);
/* attempt to acquire the name */
g_dbus_connection_invoke_method (client->connection,
"org.freedesktop.DBus", /* bus name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"RequestName", /* method name */
g_variant_new ("(su)",
client->name,
client->flags),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) request_name_cb,
client_ref (client));
}
static void
connection_get_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
Client *client = user_data;
client->connection = g_bus_get_finish (res, NULL);
if (client->connection == NULL)
{
call_lost_handler (client);
goto out;
}
/* No need to schedule this in idle as we're already in the thread
* that the user called g_bus_own_name() from. This is because
* g_bus_get() guarantees that.
*
* Also, we need to ensure that the handler is invoked *before*
* we call RequestName(). Otherwise there is a race.
*/
if (client->bus_acquired_handler != NULL)
{
client->bus_acquired_handler (client->connection,
client->name,
client->user_data);
}
has_connection (client);
out:
client_unref (client);
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_bus_own_name_on_connection:
* @connection: A #GDBusConnection that is not closed.
* @name: The well-known name to own.
* @flags: A set of flags from the #GBusNameOwnerFlags enumeration.
* @name_acquired_handler: Handler to invoke when @name is acquired or %NULL.
* @name_lost_handler: Handler to invoke when @name is lost or %NULL.
* @user_data: User data to pass to handlers.
* @user_data_free_func: Function for freeing @user_data or %NULL.
*
* Like g_bus_own_name() but takes a #GDBusConnection instead of a
* #GBusType.
*
* Returns: An identifier (never 0) that an be used with
* g_bus_unown_name() to stop owning the name.
**/
guint
g_bus_own_name_on_connection (GDBusConnection *connection,
const gchar *name,
GBusNameOwnerFlags flags,
GBusNameAcquiredCallback name_acquired_handler,
GBusNameLostCallback name_lost_handler,
gpointer user_data,
GDestroyNotify user_data_free_func)
{
Client *client;
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
g_return_val_if_fail (!g_dbus_connection_is_closed (connection), 0);
g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), 0);
G_LOCK (lock);
client = g_new0 (Client, 1);
client->ref_count = 1;
client->id = next_global_id++; /* TODO: uh oh, handle overflow */
client->name = g_strdup (name);
client->flags = flags;
client->name_acquired_handler = name_acquired_handler;
client->name_lost_handler = name_lost_handler;
client->user_data = user_data;
client->user_data_free_func = user_data_free_func;
client->main_context = g_main_context_get_thread_default ();
if (client->main_context != NULL)
g_main_context_ref (client->main_context);
client->connection = g_object_ref (connection);
if (map_id_to_client == NULL)
{
map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
}
g_hash_table_insert (map_id_to_client,
GUINT_TO_POINTER (client->id),
client);
G_UNLOCK (lock);
has_connection (client);
return client->id;
}
/**
* g_bus_own_name:
* @bus_type: The type of bus to own a name on.
* @name: The well-known name to own.
* @flags: A set of flags from the #GBusNameOwnerFlags enumeration.
* @bus_acquired_handler: Handler to invoke when connected to the bus of type @bus_type or %NULL.
* @name_acquired_handler: Handler to invoke when @name is acquired or %NULL.
* @name_lost_handler: Handler to invoke when @name is lost or %NULL.
* @user_data: User data to pass to handlers.
* @user_data_free_func: Function for freeing @user_data or %NULL.
*
* Starts acquiring @name on the bus specified by @bus_type and calls
* @name_acquired_handler and @name_lost_handler when the name is
* acquired respectively lost. Callbacks will be invoked in the <link
* linkend="g-main-context-push-thread-default">thread-default main
* loop</link> of the thread you are calling this function from.
*
* You are guaranteed that one of the @name_acquired_handler and @name_lost_handler
* callbacks will be invoked after calling this function - there are three
* possible cases:
* <itemizedlist>
* <listitem><para>
* @name_lost_handler with a %NULL connection (if a connection to the bus can't be made).
* </para></listitem>
* <listitem><para>
* @bus_acquired_handler then @name_lost_handler (if the name can't be obtained)
* </para></listitem>
* <listitem><para>
* @bus_acquired_handler then @name_acquired_handler (if the name was obtained).
* </para></listitem>
* </itemizedlist>
* When you are done owning the name, just call g_bus_unown_name()
* with the owner id this function returns.
*
* If the name is acquired or lost (for example another application
* could acquire the name if you allow replacement or the application
* currently owning the name exits), the handlers are also invoked. If the
* #GDBusConnection that is used for attempting to own the name
* closes, then @name_lost_handler is invoked since it is no
* longer possible for other processes to access the process.
*
* You cannot use g_bus_own_name() several times (unless interleaved
* with calls to g_bus_unown_name()) - only the first call will work.
*
* Another guarantee is that invocations of @name_acquired_handler
* and @name_lost_handler are guaranteed to alternate; that
* is, if @name_acquired_handler is invoked then you are
* guaranteed that the next time one of the handlers is invoked, it
* will be @name_lost_handler. The reverse is also true.
*
* If you plan on exporting objects (using e.g. g_dbus_connection_register_object()), note
* that it is generally too late to export the objects in @name_acquired_handler. Instead,
* you can do this in @bus_acquired_handler since you are guaranteed that this will
* run before @name is requested from the bus.
*
* This behavior makes it very simple to write applications that wants
* to own names and export objects, see <xref linkend="gdbus-owning-names"/>. Simply
* register objects to be exported in @bus_acquired_handler and
* unregister the objects (if any) in @name_lost_handler.
*
* Returns: An identifier (never 0) that an be used with
* g_bus_unown_name() to stop owning the name.
**/
guint
g_bus_own_name (GBusType bus_type,
const gchar *name,
GBusNameOwnerFlags flags,
GBusAcquiredCallback bus_acquired_handler,
GBusNameAcquiredCallback name_acquired_handler,
GBusNameLostCallback name_lost_handler,
gpointer user_data,
GDestroyNotify user_data_free_func)
{
Client *client;
g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0);
g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), 0);
G_LOCK (lock);
client = g_new0 (Client, 1);
client->ref_count = 1;
client->id = next_global_id++; /* TODO: uh oh, handle overflow */
client->name = g_strdup (name);
client->flags = flags;
client->bus_acquired_handler = bus_acquired_handler;
client->name_acquired_handler = name_acquired_handler;
client->name_lost_handler = name_lost_handler;
client->user_data = user_data;
client->user_data_free_func = user_data_free_func;
client->main_context = g_main_context_get_thread_default ();
if (client->main_context != NULL)
g_main_context_ref (client->main_context);
if (map_id_to_client == NULL)
{
map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
}
g_hash_table_insert (map_id_to_client,
GUINT_TO_POINTER (client->id),
client);
g_bus_get (bus_type,
NULL,
connection_get_cb,
client_ref (client));
G_UNLOCK (lock);
return client->id;
}
/**
* g_bus_unown_name:
* @owner_id: An identifier obtained from g_bus_own_name()
*
* Stops owning a name.
*/
void
g_bus_unown_name (guint owner_id)
{
Client *client;
g_return_if_fail (owner_id > 0);
client = NULL;
G_LOCK (lock);
if (owner_id == 0 || map_id_to_client == NULL ||
(client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (owner_id))) == NULL)
{
g_warning ("Invalid id %d passed to g_bus_unown_name()", owner_id);
goto out;
}
client->cancelled = TRUE;
g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (owner_id)));
out:
G_UNLOCK (lock);
/* do callback without holding lock */
if (client != NULL)
{
/* Release the name if needed */
if (client->needs_release && client->connection != NULL)
{
GVariant *result;
GError *error;
guint32 release_name_reply;
/* TODO: it kinda sucks having to do a sync call to release the name - but if
* we don't, then a subsequent grab of the name will make the bus daemon return
* IN_QUEUE which will trigger name_lost().
*
* I believe this is a bug in the bus daemon.
*/
error = NULL;
result = g_dbus_connection_invoke_method_sync (client->connection,
"org.freedesktop.DBus", /* bus name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"ReleaseName", /* method name */
g_variant_new ("(s)", client->name),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
if (result == NULL)
{
g_warning ("Error releasing name %s: %s", client->name, error->message);
g_error_free (error);
}
else
{
g_variant_get (result, "(u)", &release_name_reply);
if (release_name_reply != 1 /* DBUS_RELEASE_NAME_REPLY_RELEASED */)
{
g_warning ("Unexpected reply %d when releasing name %s", release_name_reply, client->name);
}
g_variant_unref (result);
}
}
if (client->disconnected_signal_handler_id > 0)
g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
if (client->name_acquired_subscription_id > 0)
g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id);
if (client->name_lost_subscription_id > 0)
g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id);
client->disconnected_signal_handler_id = 0;
client->name_acquired_subscription_id = 0;
client->name_lost_subscription_id = 0;
if (client->connection != NULL)
{
g_object_unref (client->connection);
client->connection = NULL;
}
client_unref (client);
}
}

88
gio/gdbusnameowning.h Normal file
View File

@ -0,0 +1,88 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_NAME_OWNING_H__
#define __G_DBUS_NAME_OWNING_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
/**
* GBusAcquiredCallback:
* @connection: The #GDBusConnection to a message bus.
* @name: The name that is requested to be owned.
* @user_data: User data passed to g_bus_own_name().
*
* Invoked when a connection to a message bus has been obtained.
*/
typedef void (*GBusAcquiredCallback) (GDBusConnection *connection,
const gchar *name,
gpointer user_data);
/**
* GBusNameAcquiredCallback:
* @connection: The #GDBusConnection on which to acquired the name.
* @name: The name being owned.
* @user_data: User data passed to g_bus_own_name() or g_bus_own_name_on_connection().
*
* Invoked when the name is acquired.
*/
typedef void (*GBusNameAcquiredCallback) (GDBusConnection *connection,
const gchar *name,
gpointer user_data);
/**
* GBusNameLostCallback:
* @connection: The #GDBusConnection on which to acquire the name or %NULL if
* the connection was disconnected.
* @name: The name being owned.
* @user_data: User data passed to g_bus_own_name() or g_bus_own_name_on_connection().
*
* Invoked when the name is lost or @connection has been closed.
*/
typedef void (*GBusNameLostCallback) (GDBusConnection *connection,
const gchar *name,
gpointer user_data);
guint g_bus_own_name (GBusType bus_type,
const gchar *name,
GBusNameOwnerFlags flags,
GBusAcquiredCallback bus_acquired_handler,
GBusNameAcquiredCallback name_acquired_handler,
GBusNameLostCallback name_lost_handler,
gpointer user_data,
GDestroyNotify user_data_free_func);
guint g_bus_own_name_on_connection (GDBusConnection *connection,
const gchar *name,
GBusNameOwnerFlags flags,
GBusNameAcquiredCallback name_acquired_handler,
GBusNameLostCallback name_lost_handler,
gpointer user_data,
GDestroyNotify user_data_free_func);
void g_bus_unown_name (guint owner_id);
G_END_DECLS
#endif /* __G_DBUS_NAME_OWNING_H__ */

620
gio/gdbusnamewatching.c Normal file
View File

@ -0,0 +1,620 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <glib/gi18n.h>
#include "gdbusutils.h"
#include "gdbusnamewatching.h"
#include "gdbuserror.h"
#include "gdbusprivate.h"
#include "gdbusconnection.h"
/**
* SECTION:gdbusnamewatching
* @title: Watching Bus Names
* @short_description: Simple API for watching bus names
* @include: gdbus/gdbus.h
*
* Convenience API for watching bus names.
*
* <example id="gdbus-watching-names"><title>Simple application watching a name</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../../gio/tests/gdbus-example-watch-name.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
*/
G_LOCK_DEFINE_STATIC (lock);
/* ---------------------------------------------------------------------------------------------------- */
typedef enum
{
PREVIOUS_CALL_NONE = 0,
PREVIOUS_CALL_APPEARED,
PREVIOUS_CALL_VANISHED,
} PreviousCall;
typedef struct
{
volatile gint ref_count;
guint id;
gchar *name;
GBusNameWatcherFlags flags;
gchar *name_owner;
GBusNameAppearedCallback name_appeared_handler;
GBusNameVanishedCallback name_vanished_handler;
gpointer user_data;
GDestroyNotify user_data_free_func;
GMainContext *main_context;
GDBusConnection *connection;
gulong disconnected_signal_handler_id;
guint name_owner_changed_subscription_id;
PreviousCall previous_call;
gboolean cancelled;
gboolean initialized;
} Client;
static guint next_global_id = 1;
static GHashTable *map_id_to_client = NULL;
static Client *
client_ref (Client *client)
{
g_atomic_int_inc (&client->ref_count);
return client;
}
static void
client_unref (Client *client)
{
if (g_atomic_int_dec_and_test (&client->ref_count))
{
if (client->connection != NULL)
{
if (client->name_owner_changed_subscription_id > 0)
g_dbus_connection_signal_unsubscribe (client->connection, client->name_owner_changed_subscription_id);
if (client->disconnected_signal_handler_id > 0)
g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
g_object_unref (client->connection);
}
g_free (client->name);
g_free (client->name_owner);
if (client->main_context != NULL)
g_main_context_unref (client->main_context);
if (client->user_data_free_func != NULL)
client->user_data_free_func (client->user_data);
g_free (client);
}
}
/* ---------------------------------------------------------------------------------------------------- */
typedef enum
{
CALL_TYPE_NAME_APPEARED,
CALL_TYPE_NAME_VANISHED
} CallType;
typedef struct
{
Client *client;
/* keep this separate because client->connection may
* be set to NULL after scheduling the call
*/
GDBusConnection *connection;
/* ditto */
gchar *name_owner;
CallType call_type;
} CallHandlerData;
static void
call_handler_data_free (CallHandlerData *data)
{
if (data->connection != NULL)
g_object_unref (data->connection);
g_free (data->name_owner);
client_unref (data->client);
g_free (data);
}
static void
actually_do_call (Client *client, GDBusConnection *connection, const gchar *name_owner, CallType call_type)
{
switch (call_type)
{
case CALL_TYPE_NAME_APPEARED:
if (client->name_appeared_handler != NULL)
{
client->name_appeared_handler (connection,
client->name,
name_owner,
client->user_data);
}
break;
case CALL_TYPE_NAME_VANISHED:
if (client->name_vanished_handler != NULL)
{
client->name_vanished_handler (connection,
client->name,
client->user_data);
}
break;
default:
g_assert_not_reached ();
break;
}
}
static gboolean
call_in_idle_cb (gpointer _data)
{
CallHandlerData *data = _data;
actually_do_call (data->client, data->connection, data->name_owner, data->call_type);
return FALSE;
}
static void
schedule_call_in_idle (Client *client, CallType call_type)
{
CallHandlerData *data;
GSource *idle_source;
data = g_new0 (CallHandlerData, 1);
data->client = client_ref (client);
data->connection = client->connection != NULL ? g_object_ref (client->connection) : NULL;
data->name_owner = g_strdup (client->name_owner);
data->call_type = call_type;
idle_source = g_idle_source_new ();
g_source_set_priority (idle_source, G_PRIORITY_HIGH);
g_source_set_callback (idle_source,
call_in_idle_cb,
data,
(GDestroyNotify) call_handler_data_free);
g_source_attach (idle_source, client->main_context);
g_source_unref (idle_source);
}
static void
do_call (Client *client, CallType call_type)
{
/* only schedule in idle if we're not in the right thread */
if (g_main_context_get_thread_default () != client->main_context)
schedule_call_in_idle (client, call_type);
else
actually_do_call (client, client->connection, client->name_owner, call_type);
}
static void
call_appeared_handler (Client *client)
{
if (client->previous_call != PREVIOUS_CALL_APPEARED)
{
client->previous_call = PREVIOUS_CALL_APPEARED;
if (!client->cancelled && client->name_appeared_handler != NULL)
{
do_call (client, CALL_TYPE_NAME_APPEARED);
}
}
}
static void
call_vanished_handler (Client *client,
gboolean ignore_cancelled)
{
if (client->previous_call != PREVIOUS_CALL_VANISHED)
{
client->previous_call = PREVIOUS_CALL_VANISHED;
if (((!client->cancelled) || ignore_cancelled) && client->name_vanished_handler != NULL)
{
do_call (client, CALL_TYPE_NAME_VANISHED);
}
}
}
/* ---------------------------------------------------------------------------------------------------- */
static void
on_connection_disconnected (GDBusConnection *connection,
gboolean remote_peer_vanished,
GError *error,
gpointer user_data)
{
Client *client = user_data;
if (client->name_owner_changed_subscription_id > 0)
g_dbus_connection_signal_unsubscribe (client->connection, client->name_owner_changed_subscription_id);
if (client->disconnected_signal_handler_id > 0)
g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
g_object_unref (client->connection);
client->disconnected_signal_handler_id = 0;
client->name_owner_changed_subscription_id = 0;
client->connection = NULL;
call_vanished_handler (client, FALSE);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
on_name_owner_changed (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
Client *client = user_data;
const gchar *name;
const gchar *old_owner;
const gchar *new_owner;
if (!client->initialized)
goto out;
if (g_strcmp0 (object_path, "/org/freedesktop/DBus") != 0 ||
g_strcmp0 (interface_name, "org.freedesktop.DBus") != 0 ||
g_strcmp0 (sender_name, "org.freedesktop.DBus") != 0)
goto out;
g_variant_get (parameters,
"(sss)",
&name,
&old_owner,
&new_owner);
/* we only care about a specific name */
if (g_strcmp0 (name, client->name) != 0)
goto out;
if ((old_owner != NULL && strlen (old_owner) > 0) && client->name_owner != NULL)
{
g_free (client->name_owner);
client->name_owner = NULL;
call_vanished_handler (client, FALSE);
}
if (new_owner != NULL && strlen (new_owner) > 0)
{
g_warn_if_fail (client->name_owner == NULL);
g_free (client->name_owner);
client->name_owner = g_strdup (new_owner);
call_appeared_handler (client);
}
out:
;
}
/* ---------------------------------------------------------------------------------------------------- */
static void
get_name_owner_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
Client *client = user_data;
GVariant *result;
const char *name_owner;
name_owner = NULL;
result = NULL;
result = g_dbus_connection_invoke_method_finish (client->connection,
res,
NULL);
if (result != NULL)
{
g_variant_get (result, "(s)", &name_owner);
}
if (name_owner != NULL)
{
g_warn_if_fail (client->name_owner == NULL);
client->name_owner = g_strdup (name_owner);
call_appeared_handler (client);
}
else
{
call_vanished_handler (client, FALSE);
}
client->initialized = TRUE;
if (result != NULL)
g_variant_unref (result);
client_unref (client);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
invoke_get_name_owner (Client *client)
{
g_dbus_connection_invoke_method (client->connection,
"org.freedesktop.DBus", /* bus name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetNameOwner", /* method name */
g_variant_new ("(s)", client->name),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) get_name_owner_cb,
client_ref (client));
}
/* ---------------------------------------------------------------------------------------------------- */
static void
start_service_by_name_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
Client *client = user_data;
GVariant *result;
result = NULL;
result = g_dbus_connection_invoke_method_finish (client->connection,
res,
NULL);
if (result != NULL)
{
guint32 start_service_result;
g_variant_get (result, "(u)", &start_service_result);
if (start_service_result == 1) /* DBUS_START_REPLY_SUCCESS */
{
invoke_get_name_owner (client);
}
else if (start_service_result == 2) /* DBUS_START_REPLY_ALREADY_RUNNING */
{
invoke_get_name_owner (client);
}
else
{
g_warning ("Unexpected reply %d from StartServiceByName() method", start_service_result);
call_vanished_handler (client, FALSE);
client->initialized = TRUE;
}
}
else
{
/* Errors are not unexpected; the bus will reply e.g.
*
* org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.Epiphany2
* was not provided by any .service files
*
* so just report vanished.
*/
call_vanished_handler (client, FALSE);
client->initialized = TRUE;
}
if (result != NULL)
g_variant_unref (result);
client_unref (client);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
has_connection (Client *client)
{
/* listen for disconnection */
client->disconnected_signal_handler_id = g_signal_connect (client->connection,
"closed",
G_CALLBACK (on_connection_disconnected),
client);
/* start listening to NameOwnerChanged messages immediately */
client->name_owner_changed_subscription_id = g_dbus_connection_signal_subscribe (client->connection,
"org.freedesktop.DBus", /* name */
"org.freedesktop.DBus", /* if */
"NameOwnerChanged", /* signal */
"/org/freedesktop/DBus", /* path */
client->name,
on_name_owner_changed,
client,
NULL);
if (client->flags & G_BUS_NAME_WATCHER_FLAGS_AUTO_START)
{
g_dbus_connection_invoke_method (client->connection,
"org.freedesktop.DBus", /* bus name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"StartServiceByName", /* method name */
g_variant_new ("(su)", client->name, 0),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) start_service_by_name_cb,
client_ref (client));
}
else
{
/* check owner */
invoke_get_name_owner (client);
}
}
static void
connection_get_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
Client *client = user_data;
client->connection = g_bus_get_finish (res, NULL);
if (client->connection == NULL)
{
call_vanished_handler (client, FALSE);
goto out;
}
has_connection (client);
out:
client_unref (client);
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_bus_watch_name:
* @bus_type: The type of bus to watch a name on.
* @name: The name (well-known or unique) to watch.
* @flags: Flags from the #GBusNameWatcherFlags enumeration.
* @name_appeared_handler: Handler to invoke when @name is known to exist or %NULL.
* @name_vanished_handler: Handler to invoke when @name is known to not exist or %NULL.
* @user_data: User data to pass to handlers.
* @user_data_free_func: Function for freeing @user_data or %NULL.
*
* Starts watching @name on the bus specified by @bus_type and calls
* @name_appeared_handler and @name_vanished_handler when the name is
* known to have a owner respectively known to lose its
* owner. Callbacks will be invoked in the <link
* linkend="g-main-context-push-thread-default">thread-default main
* loop</link> of the thread you are calling this function from.
*
* You are guaranteed that one of the handlers will be invoked after
* calling this function. When you are done watching the name, just
* call g_bus_unwatch_name() with the watcher id this function
* returns.
*
* If the name vanishes or appears (for example the application owning
* the name could restart), the handlers are also invoked. If the
* #GDBusConnection that is used for watching the name disconnects, then
* @name_vanished_handler is invoked since it is no longer
* possible to access the name.
*
* Another guarantee is that invocations of @name_appeared_handler
* and @name_vanished_handler are guaranteed to alternate; that
* is, if @name_appeared_handler is invoked then you are
* guaranteed that the next time one of the handlers is invoked, it
* will be @name_vanished_handler. The reverse is also true.
*
* This behavior makes it very simple to write applications that wants
* to take action when a certain name exists, see <xref
* linkend="gdbus-watching-names"/>. Basically, the application
* should create object proxies in @name_appeared_handler and destroy
* them again (if any) in @name_vanished_handler.
*
* Returns: An identifier (never 0) that an be used with
* g_bus_unwatch_name() to stop watching the name.
**/
guint
g_bus_watch_name (GBusType bus_type,
const gchar *name,
GBusNameWatcherFlags flags,
GBusNameAppearedCallback name_appeared_handler,
GBusNameVanishedCallback name_vanished_handler,
gpointer user_data,
GDestroyNotify user_data_free_func)
{
Client *client;
g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0);
g_return_val_if_fail (g_dbus_is_name (name), 0);
G_LOCK (lock);
client = g_new0 (Client, 1);
client->ref_count = 1;
client->id = next_global_id++; /* TODO: uh oh, handle overflow */
client->name = g_strdup (name);
client->flags = flags;
client->name_appeared_handler = name_appeared_handler;
client->name_vanished_handler = name_vanished_handler;
client->user_data = user_data;
client->user_data_free_func = user_data_free_func;
client->main_context = g_main_context_get_thread_default ();
if (client->main_context != NULL)
g_main_context_ref (client->main_context);
if (map_id_to_client == NULL)
{
map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
}
g_hash_table_insert (map_id_to_client,
GUINT_TO_POINTER (client->id),
client);
g_bus_get (bus_type,
NULL,
connection_get_cb,
client_ref (client));
G_UNLOCK (lock);
return client->id;
}
/**
* g_bus_unwatch_name:
* @watcher_id: An identifier obtained from g_bus_watch_name()
*
* Stops watching a name.
**/
void
g_bus_unwatch_name (guint watcher_id)
{
Client *client;
g_return_if_fail (watcher_id > 0);
client = NULL;
G_LOCK (lock);
if (watcher_id == 0 ||
map_id_to_client == NULL ||
(client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL)
{
g_warning ("Invalid id %d passed to g_bus_unwatch_name()", watcher_id);
goto out;
}
client->cancelled = TRUE;
g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id)));
out:
G_UNLOCK (lock);
/* do callback without holding lock */
if (client != NULL)
{
client_unref (client);
}
}

68
gio/gdbusnamewatching.h Normal file
View File

@ -0,0 +1,68 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_NAME_WATCHING_H__
#define __G_DBUS_NAME_WATCHING_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
/**
* GBusNameAppearedCallback:
* @connection: The #GDBusConnection the name is being watched on.
* @name: The name being watched.
* @name_owner: Unique name of the owner of the name being watched.
* @user_data: User data passed to g_bus_watch_name().
*
* Invoked when the name being watched is known to have to have a owner.
*/
typedef void (*GBusNameAppearedCallback) (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data);
/**
* GBusNameVanishedCallback:
* @connection: The #GDBusConnection the name is being watched on.
* @name: The name being watched.
* @user_data: User data passed to g_bus_watch_name().
*
* Invoked when the name being watched is known not to have to have a owner.
*/
typedef void (*GBusNameVanishedCallback) (GDBusConnection *connection,
const gchar *name,
gpointer user_data);
guint g_bus_watch_name (GBusType bus_type,
const gchar *name,
GBusNameWatcherFlags flags,
GBusNameAppearedCallback name_appeared_handler,
GBusNameVanishedCallback name_vanished_handler,
gpointer user_data,
GDestroyNotify user_data_free_func);
void g_bus_unwatch_name (guint watcher_id);
G_END_DECLS
#endif /* __G_DBUS_NAME_WATCHING_H__ */

1040
gio/gdbusprivate.c Normal file

File diff suppressed because it is too large Load Diff

83
gio/gdbusprivate.h Normal file
View File

@ -0,0 +1,83 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#if !defined (GIO_COMPILATION)
#error "gdbusprivate.h is a private header file."
#endif
#ifndef __G_DBUS_PRIVATE_H__
#define __G_DBUS_PRIVATE_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
/* ---------------------------------------------------------------------------------------------------- */
typedef struct GDBusWorker GDBusWorker;
typedef void (*GDBusWorkerMessageReceivedCallback) (GDBusWorker *worker,
GDBusMessage *message,
gpointer user_data);
typedef void (*GDBusWorkerDisconnectedCallback) (GDBusWorker *worker,
gboolean remote_peer_vanished,
GError *error,
gpointer user_data);
/* This function may be called from any thread - callbacks will be in the shared private message thread
* and must not block.
*/
GDBusWorker *_g_dbus_worker_new (GIOStream *stream,
GDBusCapabilityFlags capabilities,
GDBusWorkerMessageReceivedCallback message_received_callback,
GDBusWorkerDisconnectedCallback disconnected_callback,
gpointer user_data);
/* can be called from any thread - steals blob */
void _g_dbus_worker_send_message (GDBusWorker *worker,
GDBusMessage *message,
gchar *blob,
gsize blob_len);
/* can be called from any thread */
void _g_dbus_worker_stop (GDBusWorker *worker);
/* ---------------------------------------------------------------------------------------------------- */
void _g_dbus_initialize (void);
gboolean _g_dbus_debug_authentication (void);
gboolean _g_dbus_debug_message (void);
gboolean _g_dbus_address_parse_entry (const gchar *address_entry,
gchar **out_transport_name,
GHashTable **out_key_value_pairs,
GError **error);
gchar * _g_dbus_compute_complete_signature (GDBusArgInfo **args,
gboolean include_parentheses);
/* ---------------------------------------------------------------------------------------------------- */
G_END_DECLS
#endif /* __G_DBUS_PRIVATE_H__ */

1542
gio/gdbusproxy.c Normal file

File diff suppressed because it is too large Load Diff

146
gio/gdbusproxy.h Normal file
View File

@ -0,0 +1,146 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_PROXY_H__
#define __G_DBUS_PROXY_H__
#include <gio/giotypes.h>
#include <gio/gdbusintrospection.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_PROXY (g_dbus_proxy_get_type ())
#define G_DBUS_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_PROXY, GDBusProxy))
#define G_DBUS_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_PROXY, GDBusProxyClass))
#define G_DBUS_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_PROXY, GDBusProxyClass))
#define G_IS_DBUS_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_PROXY))
#define G_IS_DBUS_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_PROXY))
typedef struct _GDBusProxyClass GDBusProxyClass;
typedef struct _GDBusProxyPrivate GDBusProxyPrivate;
/**
* GDBusProxy:
*
* The #GDBusProxy structure contains only private data and
* should only be accessed using the provided API.
*/
struct _GDBusProxy
{
/*< private >*/
GObject parent_instance;
GDBusProxyPrivate *priv;
};
/**
* GDBusProxyClass:
* @g_properties_changed: Signal class handler for the #GDBusProxy::g-properties-changed signal.
* @g_signal: Signal class handler for the #GDBusProxy::g-signal signal.
*
* Class structure for #GDBusProxy.
*/
struct _GDBusProxyClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
/* Signals */
void (*g_properties_changed) (GDBusProxy *proxy,
GHashTable *changed_properties);
void (*g_signal) (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters);
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
};
GType g_dbus_proxy_get_type (void) G_GNUC_CONST;
void g_dbus_proxy_new (GDBusConnection *connection,
GType object_type,
GDBusProxyFlags flags,
GDBusInterfaceInfo *info,
const gchar *unique_bus_name,
const gchar *object_path,
const gchar *interface_name,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDBusProxy *g_dbus_proxy_new_finish (GAsyncResult *res,
GError **error);
GDBusProxy *g_dbus_proxy_new_sync (GDBusConnection *connection,
GType object_type,
GDBusProxyFlags flags,
GDBusInterfaceInfo *info,
const gchar *unique_bus_name,
const gchar *object_path,
const gchar *interface_name,
GCancellable *cancellable,
GError **error);
GDBusConnection *g_dbus_proxy_get_connection (GDBusProxy *proxy);
GDBusProxyFlags g_dbus_proxy_get_flags (GDBusProxy *proxy);
const gchar *g_dbus_proxy_get_unique_bus_name (GDBusProxy *proxy);
const gchar *g_dbus_proxy_get_object_path (GDBusProxy *proxy);
const gchar *g_dbus_proxy_get_interface_name (GDBusProxy *proxy);
gint g_dbus_proxy_get_default_timeout (GDBusProxy *proxy);
void g_dbus_proxy_set_default_timeout (GDBusProxy *proxy,
gint timeout_msec);
GDBusInterfaceInfo *g_dbus_proxy_get_interface_info (GDBusProxy *proxy);
void g_dbus_proxy_set_interface_info (GDBusProxy *proxy,
GDBusInterfaceInfo *info);
GVariant *g_dbus_proxy_get_cached_property (GDBusProxy *proxy,
const gchar *property_name,
GError **error);
gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy,
GError **error);
void g_dbus_proxy_invoke_method (GDBusProxy *proxy,
const gchar *method_name,
GVariant *parameters,
GDBusInvokeMethodFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GVariant *g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy,
GAsyncResult *res,
GError **error);
GVariant *g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy,
const gchar *method_name,
GVariant *parameters,
GDBusInvokeMethodFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_DBUS_PROXY_H__ */

397
gio/gdbusproxywatching.c Normal file
View File

@ -0,0 +1,397 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <glib/gi18n.h>
#include "gdbusutils.h"
#include "gdbusnamewatching.h"
#include "gdbusproxywatching.h"
#include "gdbuserror.h"
#include "gdbusprivate.h"
#include "gdbusproxy.h"
#include "gdbusnamewatching.h"
#include "gcancellable.h"
/**
* SECTION:gdbusproxywatching
* @title: Watching Proxies
* @short_description: Simple API for watching proxies
* @include: gdbus/gdbus.h
*
* Convenience API for watching bus proxies.
*
* <example id="gdbus-watching-proxy"><title>Simple application watching a proxy</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../../gio/tests/gdbus-example-watch-proxy.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
*/
/* ---------------------------------------------------------------------------------------------------- */
G_LOCK_DEFINE_STATIC (lock);
static guint next_global_id = 1;
static GHashTable *map_id_to_client = NULL;
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
guint id;
GBusProxyAppearedCallback proxy_appeared_handler;
GBusProxyVanishedCallback proxy_vanished_handler;
gpointer user_data;
GDestroyNotify user_data_free_func;
GMainContext *main_context;
gchar *name;
gchar *name_owner;
GDBusConnection *connection;
guint name_watcher_id;
GCancellable *cancellable;
gchar *object_path;
gchar *interface_name;
GType interface_type;
GDBusProxyFlags proxy_flags;
GDBusProxy *proxy;
gboolean initial_construction;
} Client;
static void
client_unref (Client *client)
{
/* ensure we're only called from g_bus_unwatch_proxy */
g_assert (client->name_watcher_id == 0);
g_free (client->name_owner);
if (client->connection != NULL)
g_object_unref (client->connection);
if (client->proxy != NULL)
g_object_unref (client->proxy);
g_free (client->name);
g_free (client->object_path);
g_free (client->interface_name);
if (client->main_context != NULL)
g_main_context_unref (client->main_context);
if (client->user_data_free_func != NULL)
client->user_data_free_func (client->user_data);
g_free (client);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
proxy_constructed_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
Client *client = user_data;
GDBusProxy *proxy;
GError *error;
error = NULL;
proxy = g_dbus_proxy_new_finish (res, &error);
if (proxy == NULL)
{
/* g_warning ("error while constructing proxy: %s", error->message); */
g_error_free (error);
/* handle initial construction, send out vanished if the name
* is there but we constructing a proxy fails
*/
if (client->initial_construction)
{
if (client->proxy_vanished_handler != NULL)
{
client->proxy_vanished_handler (client->connection,
client->name,
client->user_data);
}
client->initial_construction = FALSE;
}
}
else
{
g_assert (client->proxy == NULL);
g_assert (client->cancellable != NULL);
client->proxy = G_DBUS_PROXY (proxy);
g_object_unref (client->cancellable);
client->cancellable = NULL;
/* perform callback */
if (client->proxy_appeared_handler != NULL)
{
client->proxy_appeared_handler (client->connection,
client->name,
client->name_owner,
client->proxy,
client->user_data);
}
client->initial_construction = FALSE;
}
}
static void
on_name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
Client *client = user_data;
//g_debug ("\n\nname appeared (owner `%s')", name_owner);
/* invariants */
g_assert (client->name_owner == NULL);
g_assert (client->connection == NULL);
g_assert (client->cancellable == NULL);
client->name_owner = g_strdup (name_owner);
client->connection = g_object_ref (connection);
client->cancellable = g_cancellable_new ();
g_dbus_proxy_new (client->connection,
client->interface_type,
client->proxy_flags,
NULL, /* GDBusInterfaceInfo */
client->name_owner,
client->object_path,
client->interface_name,
client->cancellable,
proxy_constructed_cb,
client);
}
static void
on_name_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
Client *client = user_data;
/*g_debug ("\n\nname vanished");*/
g_free (client->name_owner);
if (client->connection != NULL)
g_object_unref (client->connection);
client->name_owner = NULL;
client->connection = NULL;
/* free the proxy if we have it */
if (client->proxy != NULL)
{
g_assert (client->cancellable == NULL);
g_object_unref (client->proxy);
client->proxy = NULL;
/* if we have the proxy, it means we last sent out a 'appeared'
* callback - so send out a 'vanished' callback
*/
if (client->proxy_vanished_handler != NULL)
{
client->proxy_vanished_handler (client->connection,
client->name,
client->user_data);
}
client->initial_construction = FALSE;
}
else
{
/* otherwise cancel construction of the proxy if applicable */
if (client->cancellable != NULL)
{
g_cancellable_cancel (client->cancellable);
g_object_unref (client->cancellable);
client->cancellable = NULL;
}
else
{
/* handle initial construction, send out vanished if
* the name isn't there
*/
if (client->initial_construction)
{
if (client->proxy_vanished_handler != NULL)
{
client->proxy_vanished_handler (client->connection,
client->name,
client->user_data);
}
client->initial_construction = FALSE;
}
}
}
}
/**
* g_bus_watch_proxy:
* @bus_type: The type of bus to watch a name on (can't be #G_BUS_TYPE_NONE).
* @name: The name (well-known or unique) to watch.
* @flags: Flags from the #GBusNameWatcherFlags enumeration.
* @object_path: The object path of the remote object to watch.
* @interface_name: The D-Bus interface name for the proxy.
* @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type.
* @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy.
* @proxy_appeared_handler: Handler to invoke when @name is known to exist and the
* requested proxy is available.
* @proxy_vanished_handler: Handler to invoke when @name is known to not exist
* and the previously created proxy is no longer available.
* @user_data: User data to pass to handlers.
* @user_data_free_func: Function for freeing @user_data or %NULL.
*
* Starts watching a remote object at @object_path owned by @name on
* the bus specified by @bus_type. When the object is available, a
* #GDBusProxy (or derived class cf. @interface_type) instance is
* constructed for the @interface_name D-Bus interface and then
* @proxy_appeared_handler will be called when the proxy is ready and
* all properties have been loaded. When @name vanishes,
* @proxy_vanished_handler is called.
*
* This function makes it very simple to write applications that wants
* to watch a well-known remote object on a well-known name, see <xref
* linkend="gdbus-watching-proxy"/>. Basically, the application simply
* starts using the proxy when @proxy_appeared_handler is called and
* stops using it when @proxy_vanished_handler is called. Callbacks
* will be invoked in the <link
* linkend="g-main-context-push-thread-default">thread-default main
* loop</link> of the thread you are calling this function from.
*
* Applications typically use this function to watch the
* <quote>manager</quote> object of a well-known name. Upon acquiring
* a proxy for the manager object, applications typically construct
* additional proxies in response to the result of enumeration methods
* on the manager object.
*
* Many of the comment that applies to g_bus_watch_name() also applies
* here. For example, you are guaranteed that one of the handlers will
* be invoked (on the main thread) after calling this function and
* also that the two handlers alternate. When you are done watching the
* proxy, just call g_bus_unwatch_proxy().
*
* Returns: An identifier (never 0) that can be used with
* g_bus_unwatch_proxy() to stop watching the remote object.
**/
guint
g_bus_watch_proxy (GBusType bus_type,
const gchar *name,
GBusNameWatcherFlags flags,
const gchar *object_path,
const gchar *interface_name,
GType interface_type,
GDBusProxyFlags proxy_flags,
GBusProxyAppearedCallback proxy_appeared_handler,
GBusProxyVanishedCallback proxy_vanished_handler,
gpointer user_data,
GDestroyNotify user_data_free_func)
{
Client *client;
g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0);
g_return_val_if_fail (g_dbus_is_name (name), 0);
g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0);
g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0);
G_LOCK (lock);
client = g_new0 (Client, 1);
client->id = next_global_id++; /* TODO: uh oh, handle overflow */
client->name = g_strdup (name);
client->proxy_appeared_handler = proxy_appeared_handler;
client->proxy_vanished_handler = proxy_vanished_handler;
client->user_data = user_data;
client->user_data_free_func = user_data_free_func;
client->main_context = g_main_context_get_thread_default ();
if (client->main_context != NULL)
g_main_context_ref (client->main_context);
client->name_watcher_id = g_bus_watch_name (bus_type,
name,
flags,
on_name_appeared,
on_name_vanished,
client,
NULL);
client->object_path = g_strdup (object_path);
client->interface_name = g_strdup (interface_name);
client->interface_type = interface_type;
client->proxy_flags = proxy_flags;
client->initial_construction = TRUE;
if (map_id_to_client == NULL)
{
map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
}
g_hash_table_insert (map_id_to_client,
GUINT_TO_POINTER (client->id),
client);
G_UNLOCK (lock);
return client->id;
}
/**
* g_bus_unwatch_proxy:
* @watcher_id: An identifier obtained from g_bus_watch_proxy()
*
* Stops watching proxy.
*/
void
g_bus_unwatch_proxy (guint watcher_id)
{
Client *client;
g_return_if_fail (watcher_id > 0);
client = NULL;
G_LOCK (lock);
if (watcher_id == 0 ||
map_id_to_client == NULL ||
(client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL)
{
g_warning ("Invalid id %d passed to g_bus_unwatch_proxy()", watcher_id);
goto out;
}
g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id)));
out:
G_UNLOCK (lock);
if (client != NULL)
{
g_bus_unwatch_name (client->name_watcher_id);
client->name_watcher_id = 0;
client_unref (client);
}
}

77
gio/gdbusproxywatching.h Normal file
View File

@ -0,0 +1,77 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_PROXY_WATCHING_H__
#define __G_DBUS_PROXY_WATCHING_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
/**
* GBusProxyAppearedCallback:
* @connection: The #GDBusConnection the proxy is being watched on.
* @name: The name being watched.
* @name_owner: Unique name of the owner of the name being watched.
* @proxy: A #GDBusProxy (or derived) instance with all properties loaded.
* @user_data: User data passed to g_bus_watch_proxy().
*
* Invoked when the proxy being watched is ready for use - the passed
* @proxy object is valid until the #GBusProxyVanishedCallback
* callback is invoked.
*/
typedef void (*GBusProxyAppearedCallback) (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy,
gpointer user_data);
/**
* GBusProxyVanishedCallback:
* @connection: The #GDBusConnection the proxy is being watched on.
* @name: The name being watched.
* @user_data: User data passed to g_bus_watch_proxy().
*
* Invoked when the proxy being watched has vanished. The #GDBusProxy
* object passed in the #GBusProxyAppearedCallback callback is no
* longer valid.
*/
typedef void (*GBusProxyVanishedCallback) (GDBusConnection *connection,
const gchar *name,
gpointer user_data);
guint g_bus_watch_proxy (GBusType bus_type,
const gchar *name,
GBusNameWatcherFlags flags,
const gchar *object_path,
const gchar *interface_name,
GType interface_type,
GDBusProxyFlags proxy_flags,
GBusProxyAppearedCallback proxy_appeared_handler,
GBusProxyVanishedCallback proxy_vanished_handler,
gpointer user_data,
GDestroyNotify user_data_free_func);
void g_bus_unwatch_proxy (guint watcher_id);
G_END_DECLS
#endif /* __G_DBUS_PROXY_WATCHING_H__ */

1043
gio/gdbusserver.c Normal file

File diff suppressed because it is too large Load Diff

97
gio/gdbusserver.h Normal file
View File

@ -0,0 +1,97 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_SERVER_H__
#define __G_DBUS_SERVER_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_SERVER (g_dbus_server_get_type ())
#define G_DBUS_SERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_SERVER, GDBusServer))
#define G_DBUS_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_SERVER, GDBusServerClass))
#define G_DBUS_SERVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_SERVER, GDBusServerClass))
#define G_IS_DBUS_SERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_SERVER))
#define G_IS_DBUS_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_SERVER))
typedef struct _GDBusServerClass GDBusServerClass;
typedef struct _GDBusServerPrivate GDBusServerPrivate;
/**
* GDBusServer:
*
* The #GDBusServer structure contains only private data and
* should only be accessed using the provided API.
*/
struct _GDBusServer
{
/*< private >*/
GObject parent_instance;
GDBusServerPrivate *priv;
};
/**
* GDBusServerClass:
* @new_connection: Signal class handler for the #GDBusServer::new-connection signal.
*
* Class structure for #GDBusServer.
*/
struct _GDBusServerClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
/* Signals */
void (*new_connection) (GDBusServer *server,
GDBusConnection *connection);
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
void (*_g_reserved7) (void);
void (*_g_reserved8) (void);
};
GType g_dbus_server_get_type (void) G_GNUC_CONST;
GDBusServer *g_dbus_server_new_sync (const gchar *address,
GDBusServerFlags flags,
const gchar *guid,
GDBusAuthObserver *observer,
GCancellable *cancellable,
GError **error);
const gchar *g_dbus_server_get_client_address (GDBusServer *server);
const gchar *g_dbus_server_get_guid (GDBusServer *server);
GDBusServerFlags g_dbus_server_get_flags (GDBusServer *server);
void g_dbus_server_start (GDBusServer *server);
void g_dbus_server_stop (GDBusServer *server);
gboolean g_dbus_server_is_active (GDBusServer *server);
G_END_DECLS
#endif /* __G_DBUS_SERVER_H__ */

364
gio/gdbusutils.c Normal file
View File

@ -0,0 +1,364 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <glib/gi18n.h>
#include "gdbusutils.h"
/**
* SECTION:gdbusutils
* @title: D-Bus Utilities
* @short_description: Various utilities related to D-Bus.
* @include: gdbus/gdbus.h
*
* Various utility routines related to D-Bus.
*/
static gboolean
is_valid_bus_name_character (gint c,
gboolean allow_hyphen)
{
return
(c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c == '_') ||
(allow_hyphen && c == '-');
}
static gboolean
is_valid_initial_bus_name_character (gint c,
gboolean allow_initial_digit,
gboolean allow_hyphen)
{
if (allow_initial_digit)
return is_valid_bus_name_character (c, allow_hyphen);
else
return
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c == '_') ||
(allow_hyphen && c == '-');
}
static gboolean
is_valid_name (const gchar *start,
guint len,
gboolean allow_initial_digit,
gboolean allow_hyphen)
{
gboolean ret;
const gchar *s;
const gchar *end;
gboolean has_dot;
ret = FALSE;
if (len == 0)
goto out;
s = start;
end = s + len;
has_dot = FALSE;
while (s != end)
{
if (*s == '.')
{
s += 1;
if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, allow_initial_digit, allow_hyphen)))
goto out;
has_dot = TRUE;
}
else if (G_UNLIKELY (!is_valid_bus_name_character (*s, allow_hyphen)))
{
goto out;
}
s += 1;
}
if (G_UNLIKELY (!has_dot))
goto out;
ret = TRUE;
out:
return ret;
}
/**
* g_dbus_is_name:
* @string: The string to check.
*
* Checks if @string is a valid D-Bus bus name (either unique or well-known).
*
* Returns: %TRUE if valid, %FALSE otherwise.
*/
gboolean
g_dbus_is_name (const gchar *string)
{
guint len;
gboolean ret;
const gchar *s;
const gchar *end;
g_return_val_if_fail (string != NULL, FALSE);
ret = FALSE;
len = strlen (string);
if (G_UNLIKELY (len == 0 || len > 255))
goto out;
s = string;
end = s + len;
if (*s == ':')
{
/* handle unique name */
if (!is_valid_name (s + 1, len - 1, TRUE, TRUE))
goto out;
ret = TRUE;
goto out;
}
else if (G_UNLIKELY (*s == '.'))
{
/* can't start with a . */
goto out;
}
else if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, FALSE, TRUE)))
goto out;
ret = is_valid_name (s + 1, len - 1, FALSE, TRUE);
out:
return ret;
}
/**
* g_dbus_is_unique_name:
* @string: The string to check.
*
* Checks if @string is a valid D-Bus unique bus name.
*
* Returns: %TRUE if valid, %FALSE otherwise.
*/
gboolean
g_dbus_is_unique_name (const gchar *string)
{
gboolean ret;
guint len;
g_return_val_if_fail (string != NULL, FALSE);
ret = FALSE;
len = strlen (string);
if (G_UNLIKELY (len == 0 || len > 255))
goto out;
if (G_UNLIKELY (*string != ':'))
goto out;
if (G_UNLIKELY (!is_valid_name (string + 1, len - 1, TRUE, TRUE)))
goto out;
ret = TRUE;
out:
return ret;
}
/**
* g_dbus_is_member_name:
* @string: The string to check.
*
* Checks if @string is a valid D-Bus member (e.g. signal or method) name.
*
* Returns: %TRUE if valid, %FALSE otherwise.
*/
gboolean
g_dbus_is_member_name (const gchar *string)
{
gboolean ret;
guint n;
ret = FALSE;
if (G_UNLIKELY (string == NULL))
goto out;
if (G_UNLIKELY (!is_valid_initial_bus_name_character (string[0], FALSE, FALSE)))
goto out;
for (n = 1; string[n] != '\0'; n++)
{
if (G_UNLIKELY (!is_valid_bus_name_character (string[n], FALSE)))
{
goto out;
}
}
ret = TRUE;
out:
return ret;
}
/**
* g_dbus_is_interface_name:
* @string: The string to check.
*
* Checks if @string is a valid D-Bus interface name.
*
* Returns: %TRUE if valid, %FALSE otherwise.
*/
gboolean
g_dbus_is_interface_name (const gchar *string)
{
guint len;
gboolean ret;
const gchar *s;
const gchar *end;
g_return_val_if_fail (string != NULL, FALSE);
ret = FALSE;
len = strlen (string);
if (G_UNLIKELY (len == 0 || len > 255))
goto out;
s = string;
end = s + len;
if (G_UNLIKELY (*s == '.'))
{
/* can't start with a . */
goto out;
}
else if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, FALSE, FALSE)))
goto out;
ret = is_valid_name (s + 1, len - 1, FALSE, FALSE);
out:
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
/* TODO: maybe move to glib? if so, it should conform to http://en.wikipedia.org/wiki/Guid and/or
* http://tools.ietf.org/html/rfc4122 - specifically it should have hyphens then.
*/
/**
* g_dbus_generate_guid:
*
* Generate a D-Bus GUID that can be used with
* e.g. g_dbus_connection_new().
*
* See the D-Bus specification regarding what strings are valid D-Bus
* GUID (for example, D-Bus GUIDs are not RFC-4122 compliant).
*
* Returns: A valid D-Bus GUID. Free with g_free().
*/
gchar *
g_dbus_generate_guid (void)
{
GString *s;
GTimeVal now;
guint32 r1;
guint32 r2;
guint32 r3;
s = g_string_new (NULL);
r1 = g_random_int ();
r2 = g_random_int ();
r3 = g_random_int ();
g_get_current_time (&now);
g_string_append_printf (s, "%08x", r1);
g_string_append_printf (s, "%08x", r2);
g_string_append_printf (s, "%08x", r3);
g_string_append_printf (s, "%08x", (guint32) now.tv_sec);
return g_string_free (s, FALSE);
}
/**
* g_dbus_is_guid:
* @string: The string to check.
*
* Checks if @string is a D-Bus GUID.
*
* See the D-Bus specification regarding what strings are valid D-Bus
* GUID (for example, D-Bus GUIDs are not RFC-4122 compliant).
*
* Returns: %TRUE if @string is a guid, %FALSE otherwise.
*/
gboolean
g_dbus_is_guid (const gchar *string)
{
gboolean ret;
guint n;
g_return_val_if_fail (string != NULL, FALSE);
ret = FALSE;
for (n = 0; n < 32; n++)
{
if (!g_ascii_isxdigit (string[n]))
goto out;
}
if (string[32] != '\0')
goto out;
ret = TRUE;
out:
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_dbus_is_activated:
*
* Determine if the process has been activated by a message bus.
*
* Returns: %TRUE if this process has been started by a message bus, %FALSE otherwise.
*/
gboolean
g_dbus_is_activated (void)
{
/* TODO: technically this will make child processes forked by us
* return TRUE too..
*/
return g_getenv ("DBUS_STARTER_BUS_TYPE") != NULL;
}
/* ---------------------------------------------------------------------------------------------------- */

42
gio/gdbusutils.h Normal file
View File

@ -0,0 +1,42 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_UTILS_H__
#define __G_DBUS_UTILS_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
gboolean g_dbus_is_guid (const gchar *string);
gchar *g_dbus_generate_guid (void);
gboolean g_dbus_is_name (const gchar *string);
gboolean g_dbus_is_unique_name (const gchar *string);
gboolean g_dbus_is_member_name (const gchar *string);
gboolean g_dbus_is_interface_name (const gchar *string);
gboolean g_dbus_is_activated (void);
G_END_DECLS
#endif /* __G_DBUS_UTILS_H__ */

View File

@ -6,3 +6,5 @@ BOOLEAN:OBJECT,OBJECT
VOID:STRING,BOXED,BOXED
BOOL:POINTER,INT
BOOL:UINT
VOID:STRING,STRING,BOXED
VOID:BOOL,BOXED

View File

@ -97,6 +97,22 @@
#include <gio/gzlibcompressor.h>
#include <gio/gzlibdecompressor.h>
#include <gio/gdbusutils.h>
#include <gio/gdbusaddress.h>
#include <gio/gdbusmessage.h>
#include <gio/gdbusconnection.h>
#include <gio/gdbuserror.h>
#include <gio/gdbusnameowning.h>
#include <gio/gdbusnamewatching.h>
#include <gio/gdbusproxywatching.h>
#include <gio/gdbusproxy.h>
#include <gio/gdbusintrospection.h>
#include <gio/gdbusmethodinvocation.h>
#include <gio/gdbusserver.h>
#include <gio/gcredentials.h>
#include <gio/gunixcredentialsmessage.h>
#include <gio/gdbusauthobserver.h>
#undef __GIO_GIO_H_INSIDE__
#endif /* __G_IO_H__ */

View File

@ -429,6 +429,11 @@ typedef enum {
* @G_IO_ERROR_ADDRESS_IN_USE: The requested address is already in use. Since 2.22
* @G_IO_ERROR_PARTIAL_INPUT: Need more input to finish operation. Since 2.24
* @G_IO_ERROR_INVALID_DATA: There input data was invalid. Since 2.24
* @G_IO_ERROR_DBUS_ERROR: A remote object generated an error that
* doesn't correspond to a locally registered #GError error
* domain. Use g_dbus_error_get_remote_error() to extract the D-Bus
* error name and g_dbus_error_strip_remote_error() to fix up the
* message so it matches what was received on the wire. Since 2.26.
*
* Error codes returned by GIO functions.
*
@ -469,7 +474,8 @@ typedef enum {
G_IO_ERROR_NOT_INITIALIZED,
G_IO_ERROR_ADDRESS_IN_USE,
G_IO_ERROR_PARTIAL_INPUT,
G_IO_ERROR_INVALID_DATA
G_IO_ERROR_INVALID_DATA,
G_IO_ERROR_DBUS_ERROR
} GIOErrorEnum;
@ -732,6 +738,370 @@ typedef enum {
G_UNIX_SOCKET_ADDRESS_ABSTRACT_PADDED
} GUnixSocketAddressType;
/**
* GBusType:
* @G_BUS_TYPE_NONE: Not a message bus connection.
* @G_BUS_TYPE_SESSION: The login session message bus.
* @G_BUS_TYPE_SYSTEM: The system-wide message bus.
* @G_BUS_TYPE_STARTER: Connect to the bus that activated the program.
*
* An enumeration to specify the type of a #GDBusConnection.
*/
typedef enum
{
G_BUS_TYPE_NONE = -1,
G_BUS_TYPE_SESSION = 0,
G_BUS_TYPE_SYSTEM = 1,
G_BUS_TYPE_STARTER = 2
} GBusType;
/**
* GBusNameOwnerFlags:
* @G_BUS_NAME_OWNER_FLAGS_NONE: No flags set.
* @G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT: Allow another message bus connection to claim the the name.
* @G_BUS_NAME_OWNER_FLAGS_REPLACE: If another message bus connection owns the name and have
* specified #G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, then take the name from the other connection.
*
* Flags used in g_bus_own_name().
*/
typedef enum
{
G_BUS_NAME_OWNER_FLAGS_NONE = 0, /*< nick=none >*/
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT = (1<<0), /*< nick=allow-replacement >*/
G_BUS_NAME_OWNER_FLAGS_REPLACE = (1<<1), /*< nick=replace >*/
} GBusNameOwnerFlags;
/**
* GBusNameWatcherFlags:
* @G_BUS_NAME_WATCHER_FLAGS_NONE: No flags set.
* @G_BUS_NAME_WATCHER_FLAGS_AUTO_START: If no-one owns the name when
* beginning to watch the name, ask the bus to launch an owner for the
* name.
*
* Flags used in g_bus_watch_name().
*/
typedef enum
{
G_BUS_NAME_WATCHER_FLAGS_NONE = 0,
G_BUS_NAME_WATCHER_FLAGS_AUTO_START = (1<<0)
} GBusNameWatcherFlags;
/**
* GDBusProxyFlags:
* @G_DBUS_PROXY_FLAGS_NONE: No flags set.
* @G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES: Don't load properties.
* @G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS: Don't connect to signals on the remote object.
*
* Flags used when constructing an instance of a #GDBusProxy derived class.
*/
typedef enum
{
G_DBUS_PROXY_FLAGS_NONE = 0, /*< nick=none >*/
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0), /*< nick=do-not-load-properties >*/
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1), /*< nick=do-not-connect-signals >*/
} GDBusProxyFlags;
/**
* GDBusError:
* @G_DBUS_ERROR_FAILED:
* A generic error; "something went wrong" - see the error message for
* more.
* @G_DBUS_ERROR_NO_MEMORY:
* There was not enough memory to complete an operation.
* @G_DBUS_ERROR_SERVICE_UNKNOWN:
* The bus doesn't know how to launch a service to supply the bus name
* you wanted.
* @G_DBUS_ERROR_NAME_HAS_NO_OWNER:
* The bus name you referenced doesn't exist (i.e. no application owns
* it).
* @G_DBUS_ERROR_NO_REPLY:
* No reply to a message expecting one, usually means a timeout occurred.
* @G_DBUS_ERROR_IO_ERROR:
* Something went wrong reading or writing to a socket, for example.
* @G_DBUS_ERROR_BAD_ADDRESS:
* A D-Bus bus address was malformed.
* @G_DBUS_ERROR_NOT_SUPPORTED:
* Requested operation isn't supported (like ENOSYS on UNIX).
* @G_DBUS_ERROR_LIMITS_EXCEEDED:
* Some limited resource is exhausted.
* @G_DBUS_ERROR_ACCESS_DENIED:
* Security restrictions don't allow doing what you're trying to do.
* @G_DBUS_ERROR_AUTH_FAILED:
* Authentication didn't work.
* @G_DBUS_ERROR_NO_SERVER:
* Unable to connect to server (probably caused by ECONNREFUSED on a
* socket).
* @G_DBUS_ERROR_TIMEOUT:
* Certain timeout errors, possibly ETIMEDOUT on a socket. Note that
* %G_DBUS_ERROR_NO_REPLY is used for message reply timeouts. Warning:
* this is confusingly-named given that %G_DBUS_ERROR_TIMED_OUT also
* exists. We can't fix it for compatibility reasons so just be
* careful.
* @G_DBUS_ERROR_NO_NETWORK:
* No network access (probably ENETUNREACH on a socket).
* @G_DBUS_ERROR_ADDRESS_IN_USE:
* Can't bind a socket since its address is in use (i.e. EADDRINUSE).
* @G_DBUS_ERROR_DISCONNECTED:
* The connection is disconnected and you're trying to use it.
* @G_DBUS_ERROR_INVALID_ARGS:
* Invalid arguments passed to a method call.
* @G_DBUS_ERROR_FILE_NOT_FOUND:
* Missing file.
* @G_DBUS_ERROR_FILE_EXISTS:
* Existing file and the operation you're using does not silently overwrite.
* @G_DBUS_ERROR_UNKNOWN_METHOD:
* Method name you invoked isn't known by the object you invoked it on.
* @G_DBUS_ERROR_TIMED_OUT:
* Certain timeout errors, e.g. while starting a service. Warning: this is
* confusingly-named given that %G_DBUS_ERROR_TIMEOUT also exists. We
* can't fix it for compatibility reasons so just be careful.
* @G_DBUS_ERROR_MATCH_RULE_NOT_FOUND:
* Tried to remove or modify a match rule that didn't exist.
* @G_DBUS_ERROR_MATCH_RULE_INVALID:
* The match rule isn't syntactically valid.
* @G_DBUS_ERROR_SPAWN_EXEC_FAILED:
* While starting a new process, the exec() call failed.
* @G_DBUS_ERROR_SPAWN_FORK_FAILED:
* While starting a new process, the fork() call failed.
* @G_DBUS_ERROR_SPAWN_CHILD_EXITED:
* While starting a new process, the child exited with a status code.
* @G_DBUS_ERROR_SPAWN_CHILD_SIGNALED:
* While starting a new process, the child exited on a signal.
* @G_DBUS_ERROR_SPAWN_FAILED:
* While starting a new process, something went wrong.
* @G_DBUS_ERROR_SPAWN_SETUP_FAILED:
* We failed to setup the environment correctly.
* @G_DBUS_ERROR_SPAWN_CONFIG_INVALID:
* We failed to setup the config parser correctly.
* @G_DBUS_ERROR_SPAWN_SERVICE_INVALID:
* Bus name was not valid.
* @G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
* Service file not found in system-services directory.
* @G_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID:
* Permissions are incorrect on the setuid helper.
* @G_DBUS_ERROR_SPAWN_FILE_INVALID:
* Service file invalid (Name, User or Exec missing).
* @G_DBUS_ERROR_SPAWN_NO_MEMORY:
* Tried to get a UNIX process ID and it wasn't available.
* @G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN:
* Tried to get a UNIX process ID and it wasn't available.
* @G_DBUS_ERROR_INVALID_SIGNATURE:
* A type signature is not valid.
* @G_DBUS_ERROR_INVALID_FILE_CONTENT:
* A file contains invalid syntax or is otherwise broken.
* @G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN:
* Asked for SELinux security context and it wasn't available.
* @G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN:
* Asked for ADT audit data and it wasn't available.
* @G_DBUS_ERROR_OBJECT_PATH_IN_USE:
* There's already an object with the requested object path.
*
* Error codes for the %G_DBUS_ERROR error domain.
*/
typedef enum
{
/* Well-known errors in the org.freedesktop.DBus.Error namespace */
G_DBUS_ERROR_FAILED, /* org.freedesktop.DBus.Error.Failed */
G_DBUS_ERROR_NO_MEMORY, /* org.freedesktop.DBus.Error.NoMemory */
G_DBUS_ERROR_SERVICE_UNKNOWN, /* org.freedesktop.DBus.Error.ServiceUnknown */
G_DBUS_ERROR_NAME_HAS_NO_OWNER, /* org.freedesktop.DBus.Error.NameHasNoOwner */
G_DBUS_ERROR_NO_REPLY, /* org.freedesktop.DBus.Error.NoReply */
G_DBUS_ERROR_IO_ERROR, /* org.freedesktop.DBus.Error.IOError */
G_DBUS_ERROR_BAD_ADDRESS, /* org.freedesktop.DBus.Error.BadAddress */
G_DBUS_ERROR_NOT_SUPPORTED, /* org.freedesktop.DBus.Error.NotSupported */
G_DBUS_ERROR_LIMITS_EXCEEDED, /* org.freedesktop.DBus.Error.LimitsExceeded */
G_DBUS_ERROR_ACCESS_DENIED, /* org.freedesktop.DBus.Error.AccessDenied */
G_DBUS_ERROR_AUTH_FAILED, /* org.freedesktop.DBus.Error.AuthFailed */
G_DBUS_ERROR_NO_SERVER, /* org.freedesktop.DBus.Error.NoServer */
G_DBUS_ERROR_TIMEOUT, /* org.freedesktop.DBus.Error.Timeout */
G_DBUS_ERROR_NO_NETWORK, /* org.freedesktop.DBus.Error.NoNetwork */
G_DBUS_ERROR_ADDRESS_IN_USE, /* org.freedesktop.DBus.Error.AddressInUse */
G_DBUS_ERROR_DISCONNECTED, /* org.freedesktop.DBus.Error.Disconnected */
G_DBUS_ERROR_INVALID_ARGS, /* org.freedesktop.DBus.Error.InvalidArgs */
G_DBUS_ERROR_FILE_NOT_FOUND, /* org.freedesktop.DBus.Error.FileNotFound */
G_DBUS_ERROR_FILE_EXISTS, /* org.freedesktop.DBus.Error.FileExists */
G_DBUS_ERROR_UNKNOWN_METHOD, /* org.freedesktop.DBus.Error.UnknownMethod */
G_DBUS_ERROR_TIMED_OUT, /* org.freedesktop.DBus.Error.TimedOut */
G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, /* org.freedesktop.DBus.Error.MatchRuleNotFound */
G_DBUS_ERROR_MATCH_RULE_INVALID, /* org.freedesktop.DBus.Error.MatchRuleInvalid */
G_DBUS_ERROR_SPAWN_EXEC_FAILED, /* org.freedesktop.DBus.Error.Spawn.ExecFailed */
G_DBUS_ERROR_SPAWN_FORK_FAILED, /* org.freedesktop.DBus.Error.Spawn.ForkFailed */
G_DBUS_ERROR_SPAWN_CHILD_EXITED, /* org.freedesktop.DBus.Error.Spawn.ChildExited */
G_DBUS_ERROR_SPAWN_CHILD_SIGNALED, /* org.freedesktop.DBus.Error.Spawn.ChildSignaled */
G_DBUS_ERROR_SPAWN_FAILED, /* org.freedesktop.DBus.Error.Spawn.Failed */
G_DBUS_ERROR_SPAWN_SETUP_FAILED, /* org.freedesktop.DBus.Error.Spawn.FailedToSetup */
G_DBUS_ERROR_SPAWN_CONFIG_INVALID, /* org.freedesktop.DBus.Error.Spawn.ConfigInvalid */
G_DBUS_ERROR_SPAWN_SERVICE_INVALID, /* org.freedesktop.DBus.Error.Spawn.ServiceNotValid */
G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND, /* org.freedesktop.DBus.Error.Spawn.ServiceNotFound */
G_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID, /* org.freedesktop.DBus.Error.Spawn.PermissionsInvalid */
G_DBUS_ERROR_SPAWN_FILE_INVALID, /* org.freedesktop.DBus.Error.Spawn.FileInvalid */
G_DBUS_ERROR_SPAWN_NO_MEMORY, /* org.freedesktop.DBus.Error.Spawn.NoMemory */
G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, /* org.freedesktop.DBus.Error.UnixProcessIdUnknown */
G_DBUS_ERROR_INVALID_SIGNATURE, /* org.freedesktop.DBus.Error.InvalidSignature */
G_DBUS_ERROR_INVALID_FILE_CONTENT, /* org.freedesktop.DBus.Error.InvalidFileContent */
G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN, /* org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown */
G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN, /* org.freedesktop.DBus.Error.AdtAuditDataUnknown */
G_DBUS_ERROR_OBJECT_PATH_IN_USE, /* org.freedesktop.DBus.Error.ObjectPathInUse */
} GDBusError;
/* TODO: remember to update g_dbus_error_quark() in gdbuserror.c if you extend this enumeration */
/**
* GDBusConnectionFlags:
* @G_DBUS_CONNECTION_FLAGS_NONE: No flags set.
* @G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT: Perform authentication against server.
* @G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER: Perform authentication against client.
* @G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS: When
* authenticating as a server, allow the anonymous authentication
* method.
* @G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION: Pass this flag if connecting to a peer that is a
* message bus. This means that the Hello() method will be invoked as part of the connection setup.
*
* Flags used when creating a new #GDBusConnection.
*/
typedef enum {
G_DBUS_CONNECTION_FLAGS_NONE = 0,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT = (1<<0),
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER = (1<<1),
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS = (1<<2),
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION = (1<<3)
} GDBusConnectionFlags;
/**
* GDBusCapabilityFlags:
* @G_DBUS_CAPABILITY_FLAGS_NONE: No flags set.
* @G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING: The connection
* supports exchanging UNIX file descriptors with the remote peer.
*
* Capabilities negotiated with the remote peer.
*/
typedef enum {
G_DBUS_CAPABILITY_FLAGS_NONE = 0,
G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING = (1<<0),
} GDBusCapabilityFlags;
/**
* GDBusInvokeMethodFlags:
* @G_DBUS_INVOKE_METHOD_FLAGS_NONE: No flags set.
* @G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START: The bus must not launch
* an owner for the destination name in response to this method
* invocation.
*
* Flags used in g_dbus_connection_invoke_method() and similar APIs.
*/
typedef enum {
G_DBUS_INVOKE_METHOD_FLAGS_NONE = 0,
G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START = (1<<0),
} GDBusInvokeMethodFlags;
/**
* GDBusMessageType:
* @G_DBUS_MESSAGE_TYPE_INVALID: Message is of invalid type.
* @G_DBUS_MESSAGE_TYPE_METHOD_CALL: Method call.
* @G_DBUS_MESSAGE_TYPE_METHOD_RETURN: Method reply.
* @G_DBUS_MESSAGE_TYPE_ERROR: Error reply.
* @G_DBUS_MESSAGE_TYPE_SIGNAL: Signal emission.
*
* Message types used in #GDBusMessage.
*/
typedef enum {
G_DBUS_MESSAGE_TYPE_INVALID,
G_DBUS_MESSAGE_TYPE_METHOD_CALL,
G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
G_DBUS_MESSAGE_TYPE_ERROR,
G_DBUS_MESSAGE_TYPE_SIGNAL
} GDBusMessageType;
/**
* GDBusMessageFlags:
* @G_DBUS_MESSAGE_FLAGS_NONE: No flags set.
* @G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED: A reply is not expected.
* @G_DBUS_MESSAGE_FLAGS_NO_AUTO_START: The bus must not launch an
* owner for the destination name in response to this message.
*
* Message flags used in #GDBusMessage.
*/
typedef enum {
G_DBUS_MESSAGE_FLAGS_NONE = 0,
G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED = (1<<0),
G_DBUS_MESSAGE_FLAGS_NO_AUTO_START = (1<<1)
} GDBusMessageFlags;
/**
* GDBusMessageHeaderField:
* @G_DBUS_MESSAGE_HEADER_FIELD_INVALID: Not a valid header field.
* @G_DBUS_MESSAGE_HEADER_FIELD_PATH: The object path.
* @G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE: The interface name.
* @G_DBUS_MESSAGE_HEADER_FIELD_MEMBER: The method or signal name.
* @G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME: The name of the error that occurred.
* @G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL: The serial number the message is a reply to.
* @G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION: The name the message is intended for.
* @G_DBUS_MESSAGE_HEADER_FIELD_SENDER: Unique name of the sender of the message (filled in by the bus).
* @G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE: The signature of the message body.
* @G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS: The number of UNIX file descriptors that accompany the message.
*
* Header fields used in #GDBusMessage.
*/
typedef enum {
G_DBUS_MESSAGE_HEADER_FIELD_INVALID,
G_DBUS_MESSAGE_HEADER_FIELD_PATH,
G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE,
G_DBUS_MESSAGE_HEADER_FIELD_MEMBER,
G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME,
G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL,
G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION,
G_DBUS_MESSAGE_HEADER_FIELD_SENDER,
G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE,
G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS
} GDBusMessageHeaderField;
/**
* GDBusPropertyInfoFlags:
* @G_DBUS_PROPERTY_INFO_FLAGS_NONE: No flags set.
* @G_DBUS_PROPERTY_INFO_FLAGS_READABLE: Property is readable.
* @G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE: Property is writable.
*
* Flags describing the access control of a D-Bus property.
*/
typedef enum
{
G_DBUS_PROPERTY_INFO_FLAGS_NONE = 0,
G_DBUS_PROPERTY_INFO_FLAGS_READABLE = (1<<0),
G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE = (1<<1),
} GDBusPropertyInfoFlags;
/**
* GDBusSubtreeFlags:
* @G_DBUS_SUBTREE_FLAGS_NONE: No flags set.
* @G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES: Method calls to objects not in the enumerated range
* will still be dispatched. This is useful if you want
* to dynamically spawn objects in the subtree.
*
* Flags passed to g_dbus_connection_register_subtree().
*/
typedef enum
{
G_DBUS_SUBTREE_FLAGS_NONE = 0,
G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES = (1<<0),
} GDBusSubtreeFlags;
/**
* GDBusServerFlags:
* @G_DBUS_SERVER_FLAGS_NONE: No flags set.
* @G_DBUS_SERVER_FLAGS_RUN_IN_THREAD: All #GDBusServer::new-connection
* signals will run in separated dedicated threads (see signal for
* details).
* @G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS: Allow the anonymous
* authentication method.
*
* Flags used when creating a #GDBusServer.
*/
typedef enum
{
G_DBUS_SERVER_FLAGS_NONE = 0,
G_DBUS_SERVER_FLAGS_RUN_IN_THREAD = (1<<0),
G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS = (1<<1)
} GDBusServerFlags;
G_END_DECLS
#endif /* __GIO_ENUMS_H__ */

View File

@ -333,6 +333,25 @@ struct _GOutputVector {
gsize size;
};
typedef struct _GCredentials GCredentials;
typedef struct _GUnixCredentialsMessage GUnixCredentialsMessage;
typedef struct _GDBusMessage GDBusMessage;
typedef struct _GDBusConnection GDBusConnection;
typedef struct _GMessageBusConnection GMessageBusConnection;
typedef struct _GDBusProxy GDBusProxy;
typedef struct _GDBusMethodInvocation GDBusMethodInvocation;
typedef struct _GDBusServer GDBusServer;
typedef struct _GDBusAuthObserver GDBusAuthObserver;
typedef struct _GDBusErrorEntry GDBusErrorEntry;
typedef struct _GDBusInterfaceVTable GDBusInterfaceVTable;
typedef struct _GDBusSubtreeVTable GDBusSubtreeVTable;
typedef struct _GDBusAnnotationInfo GDBusAnnotationInfo;
typedef struct _GDBusArgInfo GDBusArgInfo;
typedef struct _GDBusMethodInfo GDBusMethodInfo;
typedef struct _GDBusSignalInfo GDBusSignalInfo;
typedef struct _GDBusPropertyInfo GDBusPropertyInfo;
typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo;
typedef struct _GDBusNodeInfo GDBusNodeInfo;
G_END_DECLS

View File

@ -0,0 +1,341 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2010 Red Hat, Inc.
* Copyright (C) 2009 Codethink Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Authors: David Zeuthen <davidz@redhat.com>
*/
/**
* SECTION: gunixcredentialsmessage
* @title: GUnixCredentialsMessage
* @short_description: A GSocketControlMessage containing credentials
* @see_also: #GUnixConnection, #GSocketControlMessage
*
* This #GSocketControlMessage contains a #GCredentials instance. It
* may be sent using g_socket_send_message() and received using
* g_socket_receive_message() over UNIX sockets (ie: sockets in the
* %G_SOCKET_ADDRESS_UNIX family).
*
* For an easier way to send and receive credentials over
* stream-oriented UNIX sockets, see g_unix_connection_send_credentials() and
* g_unix_connection_receive_credentials().
**/
#include "config.h"
#include <glib/gi18n.h>
/* ---------------------------------------------------------------------------------------------------- */
#ifdef __linux__
#define _GNU_SOURCE
#define __USE_GNU
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
#define G_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
#else
/* TODO: please add support for your UNIX flavor */
#define G_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 0
#endif
/* ---------------------------------------------------------------------------------------------------- */
#include <string.h>
#include <errno.h>
#include "gunixcredentialsmessage.h"
#include "gcredentials.h"
struct _GUnixCredentialsMessagePrivate
{
GCredentials *credentials;
};
enum
{
PROP_0,
PROP_CREDENTIALS
};
G_DEFINE_TYPE (GUnixCredentialsMessage, g_unix_credentials_message, G_TYPE_SOCKET_CONTROL_MESSAGE);
static gsize
g_unix_credentials_message_get_size (GSocketControlMessage *message)
{
#ifdef __linux__
return sizeof (struct ucred);
#else
return 0;
#endif
}
static int
g_unix_credentials_message_get_level (GSocketControlMessage *message)
{
return SOL_SOCKET;
}
static int
g_unix_credentials_message_get_msg_type (GSocketControlMessage *message)
{
#ifdef __linux__
return SCM_CREDENTIALS;
#else
return 0;
#endif
}
static GSocketControlMessage *
g_unix_credentials_message_deserialize (gint level,
gint type,
gsize size,
gpointer data)
{
GSocketControlMessage *message;
message = NULL;
#ifdef __linux__
{
GCredentials *credentials;
struct ucred *ucred;
if (level != SOL_SOCKET || type != SCM_CREDENTIALS)
goto out;
if (size != sizeof (struct ucred))
{
g_warning ("Expected a struct ucred (%" G_GSIZE_FORMAT " bytes) but "
"got %" G_GSIZE_FORMAT " bytes of data",
sizeof (struct ucred),
size);
goto out;
}
ucred = data;
credentials = g_credentials_new ();
g_credentials_set_unix_user (credentials, ucred->uid);
g_credentials_set_unix_group (credentials, ucred->gid);
g_credentials_set_unix_process (credentials, ucred->pid);
message = g_unix_credentials_message_new_with_credentials (credentials);
g_object_unref (credentials);
out:
;
}
#endif
return message;
}
static void
g_unix_credentials_message_serialize (GSocketControlMessage *_message,
gpointer data)
{
GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (_message);
#ifdef __linux__
{
struct ucred *ucred = data;
ucred->uid = g_credentials_get_unix_user (message->priv->credentials);
ucred->gid = g_credentials_get_unix_group (message->priv->credentials);
ucred->pid = g_credentials_get_unix_process (message->priv->credentials);
}
#endif
}
static void
g_unix_credentials_message_finalize (GObject *object)
{
GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object);
if (message->priv->credentials != NULL)
g_object_unref (message->priv->credentials);
if (G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->finalize != NULL)
G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->finalize (object);
}
static void
g_unix_credentials_message_init (GUnixCredentialsMessage *message)
{
message->priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
G_TYPE_UNIX_CREDENTIALS_MESSAGE,
GUnixCredentialsMessagePrivate);
}
static void
g_unix_credentials_message_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object);
switch (prop_id)
{
case PROP_CREDENTIALS:
g_value_set_object (value, message->priv->credentials);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_unix_credentials_message_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object);
switch (prop_id)
{
case PROP_CREDENTIALS:
message->priv->credentials = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_unix_credentials_message_constructed (GObject *object)
{
GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object);
if (message->priv->credentials == NULL)
message->priv->credentials = g_credentials_new_for_process ();
if (G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->constructed != NULL)
G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->constructed (object);
}
static void
g_unix_credentials_message_class_init (GUnixCredentialsMessageClass *class)
{
GSocketControlMessageClass *scm_class;
GObjectClass *gobject_class;
g_type_class_add_private (class, sizeof (GUnixCredentialsMessagePrivate));
gobject_class = G_OBJECT_CLASS (class);
gobject_class->get_property = g_unix_credentials_message_get_property;
gobject_class->set_property = g_unix_credentials_message_set_property;
gobject_class->finalize = g_unix_credentials_message_finalize;
gobject_class->constructed = g_unix_credentials_message_constructed;
scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class);
scm_class->get_size = g_unix_credentials_message_get_size;
scm_class->get_level = g_unix_credentials_message_get_level;
scm_class->get_type = g_unix_credentials_message_get_msg_type;
scm_class->serialize = g_unix_credentials_message_serialize;
scm_class->deserialize = g_unix_credentials_message_deserialize;
/**
* GUnixCredentialsMessage:credentials:
*
* The credentials stored in the message.
*/
g_object_class_install_property (gobject_class,
PROP_CREDENTIALS,
g_param_spec_object ("credentials",
_("Credentials"),
_("The credentials stored in the message"),
G_TYPE_CREDENTIALS,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_unix_credentials_message_is_supported:
*
* Checks if passing a #GCredential on a #GSocket is supported on this platform.
*
* Returns: %TRUE if supported, %FALSE otherwise
*
* Since: 2.26
*/
gboolean
g_unix_credentials_message_is_supported (void)
{
return G_UNIX_CREDENTIALS_MESSAGE_SUPPORTED;
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_unix_credentials_message_new:
*
* Creates a new #GUnixCredentialsMessage with credentials matching the current processes.
*
* Returns: a new #GUnixCredentialsMessage
*
* Since: 2.26
*/
GSocketControlMessage *
g_unix_credentials_message_new (void)
{
g_return_val_if_fail (g_unix_credentials_message_is_supported (), NULL);
return g_object_new (G_TYPE_UNIX_CREDENTIALS_MESSAGE,
NULL);
}
/**
* g_unix_credentials_message_new:
* @credentials: A #GCredentials object.
*
* Creates a new #GUnixCredentialsMessage holding @credentials.
*
* Returns: a new #GUnixCredentialsMessage
*
* Since: 2.26
*/
GSocketControlMessage *
g_unix_credentials_message_new_with_credentials (GCredentials *credentials)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
g_return_val_if_fail (g_unix_credentials_message_is_supported (), NULL);
return g_object_new (G_TYPE_UNIX_CREDENTIALS_MESSAGE,
"credentials", credentials,
NULL);
}
/**
* g_unix_credentials_message_get_credentials:
* @message: A #GUnixCredentialsMessage.
*
* Gets the credentials stored in @message.
*
* Returns: A #GCredentials instance. Do not free, it is owned by @message.
*/
GCredentials *
g_unix_credentials_message_get_credentials (GUnixCredentialsMessage *message)
{
g_return_val_if_fail (G_IS_UNIX_CREDENTIALS_MESSAGE (message), NULL);
return message->priv->credentials;
}

View File

@ -0,0 +1,68 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2010 Red Hat, Inc.
* Copyright (C) 2009 Codethink Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Authors: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_UNIX_CREDENTIALS_MESSAGE_H__
#define __G_UNIX_CREDENTIALS_MESSAGE_H__
#include <gio/giotypes.h>
#include <gio/gsocketcontrolmessage.h>
G_BEGIN_DECLS
#define G_TYPE_UNIX_CREDENTIALS_MESSAGE (g_unix_credentials_message_get_type ())
#define G_UNIX_CREDENTIALS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNIX_CREDENTIALS_MESSAGE, GUnixCredentialsMessage))
#define G_UNIX_CREDENTIALS_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), G_TYPE_UNIX_CREDENTIALS_MESSAGE, GUnixCredentialsMessageClass))
#define G_IS_UNIX_CREDENTIALS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNIX_CREDENTIALS_MESSAGE))
#define G_IS_UNIX_CREDENTIALS_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), G_TYPE_UNIX_CREDENTIALS_MESSAGE))
#define G_UNIX_CREDENTIALS_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_UNIX_CREDENTIALS_MESSAGE, GUnixCredentialsMessageClass))
typedef struct _GUnixCredentialsMessagePrivate GUnixCredentialsMessagePrivate;
typedef struct _GUnixCredentialsMessageClass GUnixCredentialsMessageClass;
struct _GUnixCredentialsMessageClass
{
GSocketControlMessageClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
};
struct _GUnixCredentialsMessage
{
GSocketControlMessage parent_instance;
GUnixCredentialsMessagePrivate *priv;
};
GType g_unix_credentials_message_get_type (void) G_GNUC_CONST;
GSocketControlMessage *g_unix_credentials_message_new (void);
GSocketControlMessage *g_unix_credentials_message_new_with_credentials (GCredentials *credentials);
GCredentials *g_unix_credentials_message_get_credentials (GUnixCredentialsMessage *message);
gboolean g_unix_credentials_message_is_supported (void);
G_END_DECLS
#endif /* __G_UNIX_CREDENTIALS_MESSAGE_H__ */

View File

@ -1,3 +1,5 @@
NULL =
include $(top_srcdir)/Makefile.decl
INCLUDES = \
@ -35,19 +37,39 @@ TEST_PROGS += \
srvtarget \
contexts \
gsettings \
gschema-compile
gschema-compile \
gdbus-addresses \
gdbus-connection \
gdbus-names \
gdbus-proxy \
gdbus-introspection \
gdbus-threading \
gdbus-export \
gdbus-error \
gdbus-peer \
gdbus-exit-on-close \
$(NULL)
SAMPLE_PROGS = \
resolver \
socket-server \
socket-client \
echo-server \
httpd \
send-data \
filter-cat \
gdbus-example-own-name \
gdbus-example-watch-name \
gdbus-example-watch-proxy \
gdbus-example-server \
gdbus-example-subtree \
gdbus-example-peer \
$(NULL)
SAMPLE_PROGS = \
resolver \
socket-server \
socket-client \
echo-server \
httpd \
send-data \
filter-cat
if OS_UNIX
TEST_PROGS += live-g-file desktop-app-info unix-fd #unix-streams
SAMPLE_PROGS += gdbus-example-unix-fd-client
endif
if OS_WIN32
@ -150,6 +172,64 @@ gsettings_LDADD = $(progs_ldadd)
gschema_compile_SOURCES = gschema-compile.c
gschema_compile_LDADD = $(progs_ldadd)
if HAVE_DBUS1
TEST_PROGS += gdbus-serialization
gdbus_serialization_SOURCES = gdbus-serialization.c gdbus-tests.h gdbus-tests.c
gdbus_serialization_CFLAGS = $(DBUS1_CFLAGS)
gdbus_serialization_LDADD = $(progs_ldadd) $(DBUS1_LIBS)
endif
gdbus_addresses_SOURCES = gdbus-addresses.c
gdbus_addresses_LDADD = $(progs_ldadd)
gdbus_connection_SOURCES = gdbus-connection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_connection_LDADD = $(progs_ldadd)
gdbus_names_SOURCES = gdbus-names.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_names_LDADD = $(progs_ldadd)
gdbus_proxy_SOURCES = gdbus-proxy.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_proxy_LDADD = $(progs_ldadd)
gdbus_introspection_SOURCES = gdbus-introspection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_introspection_LDADD = $(progs_ldadd)
gdbus_threading_SOURCES = gdbus-threading.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_threading_LDADD = $(progs_ldadd)
gdbus_export_SOURCES = gdbus-export.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_export_LDADD = $(progs_ldadd)
gdbus_error_SOURCES = gdbus-error.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_error_LDADD = $(progs_ldadd)
gdbus_peer_SOURCES = gdbus-peer.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_peer_LDADD = $(progs_ldadd)
gdbus_exit_on_close_SOURCES = gdbus-exit-on-close.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
gdbus_exit_on_close_LDADD = $(progs_ldadd)
gdbus_example_watch_name_SOURCES = gdbus-example-watch-name.c
gdbus_example_watch_name_LDADD = $(progs_ldadd)
gdbus_example_watch_proxy_SOURCES = gdbus-example-watch-proxy.c
gdbus_example_watch_proxy_LDADD = $(progs_ldadd)
gdbus_example_own_name_SOURCES = gdbus-example-own-name.c
gdbus_example_own_name_LDADD = $(progs_ldadd)
gdbus_example_server_SOURCES = gdbus-example-server.c
gdbus_example_server_LDADD = $(progs_ldadd)
gdbus_example_unix_fd_client_SOURCES = gdbus-example-unix-fd-client.c
gdbus_example_unix_fd_client_LDADD = $(progs_ldadd)
gdbus_example_subtree_SOURCES = gdbus-example-subtree.c
gdbus_example_subtree_LDADD = $(progs_ldadd)
gdbus_example_peer_SOURCES = gdbus-example-peer.c
gdbus_example_peer_LDADD = $(progs_ldadd)
EXTRA_DIST += \
socket-common.c \
org.gtk.test.gschema \

View File

@ -0,0 +1,77 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#ifdef G_OS_UNIX
#include <gio/gunixsocketaddress.h>
#endif
/* ---------------------------------------------------------------------------------------------------- */
#ifdef G_OS_UNIX
static void
test_unix_address (void)
{
g_assert (!g_dbus_is_supported_address ("some-imaginary-transport:foo=bar", NULL));
g_assert (g_dbus_is_supported_address ("unix:path=/tmp/dbus-test", NULL));
g_assert (g_dbus_is_supported_address ("unix:abstract=/tmp/dbus-another-test", NULL));
g_assert (g_dbus_is_address ("unix:foo=bar"));
g_assert (!g_dbus_is_supported_address ("unix:foo=bar", NULL));
g_assert (!g_dbus_is_address ("unix:path=/foo;abstract=/bar"));
g_assert (!g_dbus_is_supported_address ("unix:path=/foo;abstract=/bar", NULL));
g_assert (g_dbus_is_supported_address ("unix:path=/tmp/concrete;unix:abstract=/tmp/abstract", NULL));
g_assert (g_dbus_is_address ("some-imaginary-transport:foo=bar"));
g_assert (g_dbus_is_address ("some-imaginary-transport:foo=bar;unix:path=/this/is/valid"));
g_assert (!g_dbus_is_supported_address ("some-imaginary-transport:foo=bar;unix:path=/this/is/valid", NULL));
}
#endif
static void
test_nonce_tcp_address (void)
{
g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar", NULL));
g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=ipv6", NULL));
g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=ipv4", NULL));
g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=blah", NULL));
g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=420000,noncefile=/foo/bar,family=ipv4", NULL));
g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=x42,noncefile=/foo/bar,family=ipv4", NULL));
g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=42x,noncefile=/foo/bar,family=ipv4", NULL));
g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=420000,noncefile=/foo/bar,family=ipv4", NULL));
}
int
main (int argc,
char *argv[])
{
g_type_init ();
g_test_init (&argc, &argv, NULL);
#ifdef G_OS_UNIX
g_test_add_func ("/gdbus/unix-address", test_unix_address);
#endif
g_test_add_func ("/gdbus/nonce-tcp-address", test_nonce_tcp_address);
return g_test_run();
}

View File

@ -0,0 +1,653 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include <string.h>
#include "gdbus-tests.h"
/* all tests rely on a shared mainloop */
static GMainLoop *loop = NULL;
/* ---------------------------------------------------------------------------------------------------- */
/* Connection life-cycle testing */
/* ---------------------------------------------------------------------------------------------------- */
static void
test_connection_life_cycle (void)
{
GDBusConnection *c;
GDBusConnection *c2;
GError *error;
error = NULL;
/*
* Check for correct behavior when no bus is present
*
*/
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
_g_assert_error_domain (error, G_IO_ERROR);
g_assert (!g_dbus_error_is_remote_error (error));
g_assert (c == NULL);
g_error_free (error);
error = NULL;
/*
* Check for correct behavior when a bus is present
*/
session_bus_up ();
/* case 1 */
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (c != NULL);
g_assert (!g_dbus_connection_is_closed (c));
/*
* Check that singleton handling work
*/
c2 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (c2 != NULL);
g_assert (c == c2);
g_object_unref (c2);
/*
* Check that private connections work
*/
c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (c2 != NULL);
g_assert (c != c2);
g_object_unref (c2);
c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (c2 != NULL);
g_assert (!g_dbus_connection_is_closed (c2));
g_dbus_connection_close (c2);
_g_assert_signal_received (c2, "closed");
g_assert (g_dbus_connection_is_closed (c2));
g_object_unref (c2);
/*
* Check for correct behavior when the bus goes away
*
*/
g_assert (!g_dbus_connection_is_closed (c));
g_dbus_connection_set_exit_on_close (c, FALSE);
session_bus_down ();
if (!g_dbus_connection_is_closed (c))
_g_assert_signal_received (c, "closed");
g_assert (g_dbus_connection_is_closed (c));
_g_object_wait_for_single_ref (c);
g_object_unref (c);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that sending and receiving messages work as expected */
/* ---------------------------------------------------------------------------------------------------- */
static void
msg_cb_expect_error_disconnected (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_invoke_method_finish (connection,
res,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
g_assert (!g_dbus_error_is_remote_error (error));
g_error_free (error);
g_assert (result == NULL);
g_main_loop_quit (loop);
}
static void
msg_cb_expect_error_unknown_method (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_invoke_method_finish (connection,
res,
&error);
g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
g_assert (g_dbus_error_is_remote_error (error));
g_assert (result == NULL);
g_main_loop_quit (loop);
}
static void
msg_cb_expect_success (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_invoke_method_finish (connection,
res,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_variant_unref (result);
g_main_loop_quit (loop);
}
static void
msg_cb_expect_error_cancelled (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_invoke_method_finish (connection,
res,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_assert (!g_dbus_error_is_remote_error (error));
g_error_free (error);
g_assert (result == NULL);
g_main_loop_quit (loop);
}
static void
msg_cb_expect_error_cancelled_2 (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_invoke_method_finish (connection,
res,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_assert (!g_dbus_error_is_remote_error (error));
g_error_free (error);
g_assert (result == NULL);
g_main_loop_quit (loop);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
test_connection_send (void)
{
GDBusConnection *c;
GCancellable *ca;
session_bus_up ();
/* First, get an unopened connection */
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (c != NULL);
g_assert (!g_dbus_connection_is_closed (c));
/*
* Check that we never actually send a message if the GCancellable
* is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED
* when the actual connection is not up.
*/
ca = g_cancellable_new ();
g_cancellable_cancel (ca);
g_dbus_connection_invoke_method (c,
"org.freedesktop.DBus", /* bus_name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetId", /* method name */
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
ca,
(GAsyncReadyCallback) msg_cb_expect_error_cancelled,
NULL);
g_main_loop_run (loop);
g_object_unref (ca);
/*
* Check that we get a reply to the GetId() method call.
*/
g_dbus_connection_invoke_method (c,
"org.freedesktop.DBus", /* bus_name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetId", /* method name */
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) msg_cb_expect_success,
NULL);
g_main_loop_run (loop);
/*
* Check that we get an error reply to the NonExistantMethod() method call.
*/
g_dbus_connection_invoke_method (c,
"org.freedesktop.DBus", /* bus_name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"NonExistantMethod", /* method name */
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) msg_cb_expect_error_unknown_method,
NULL);
g_main_loop_run (loop);
/*
* Check that cancellation works when the message is already in flight.
*/
ca = g_cancellable_new ();
g_dbus_connection_invoke_method (c,
"org.freedesktop.DBus", /* bus_name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetId", /* method name */
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
ca,
(GAsyncReadyCallback) msg_cb_expect_error_cancelled_2,
NULL);
g_cancellable_cancel (ca);
g_main_loop_run (loop);
g_object_unref (ca);
/*
* Check that we get an error when sending to a connection that is disconnected.
*/
g_dbus_connection_set_exit_on_close (c, FALSE);
session_bus_down ();
_g_assert_signal_received (c, "closed");
g_assert (g_dbus_connection_is_closed (c));
g_dbus_connection_invoke_method (c,
"org.freedesktop.DBus", /* bus_name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetId", /* method name */
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) msg_cb_expect_error_disconnected,
NULL);
g_main_loop_run (loop);
_g_object_wait_for_single_ref (c);
g_object_unref (c);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Connection signal tests */
/* ---------------------------------------------------------------------------------------------------- */
static void
test_connection_signal_handler (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
gint *counter = user_data;
*counter += 1;
/*g_debug ("in test_connection_signal_handler (sender=%s path=%s interface=%s member=%s)",
sender_name,
object_path,
interface_name,
signal_name);*/
g_main_loop_quit (loop);
}
static gboolean
test_connection_signal_quit_mainloop (gpointer user_data)
{
gboolean *quit_mainloop_fired = user_data;
*quit_mainloop_fired = TRUE;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_connection_signals (void)
{
GDBusConnection *c1;
GDBusConnection *c2;
GDBusConnection *c3;
guint s1;
guint s2;
guint s3;
gint count_s1;
gint count_s2;
gint count_name_owner_changed;
GError *error;
gboolean ret;
GVariant *result;
error = NULL;
/*
* Bring up first separate connections
*/
session_bus_up ();
/* if running with dbus-monitor, it claims the name :1.0 - so if we don't run with the monitor
* emulate this
*/
if (g_getenv ("G_DBUS_MONITOR") == NULL)
{
c1 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (c1 != NULL);
g_assert (!g_dbus_connection_is_closed (c1));
g_object_unref (c1);
}
c1 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (c1 != NULL);
g_assert (!g_dbus_connection_is_closed (c1));
g_assert_cmpstr (g_dbus_connection_get_unique_name (c1), ==, ":1.1");
/*
* Install two signal handlers for the first connection
*
* - Listen to the signal "Foo" from :1.2 (e.g. c2)
* - Listen to the signal "Foo" from anyone (e.g. both c2 and c3)
*
* and then count how many times this signal handler was invoked.
*/
s1 = g_dbus_connection_signal_subscribe (c1,
":1.2",
"org.gtk.GDBus.ExampleInterface",
"Foo",
"/org/gtk/GDBus/ExampleInterface",
NULL,
test_connection_signal_handler,
&count_s1,
NULL);
s2 = g_dbus_connection_signal_subscribe (c1,
NULL, /* match any sender */
"org.gtk.GDBus.ExampleInterface",
"Foo",
"/org/gtk/GDBus/ExampleInterface",
NULL,
test_connection_signal_handler,
&count_s2,
NULL);
s3 = g_dbus_connection_signal_subscribe (c1,
"org.freedesktop.DBus", /* sender */
"org.freedesktop.DBus", /* interface */
"NameOwnerChanged", /* member */
"/org/freedesktop/DBus", /* path */
NULL,
test_connection_signal_handler,
&count_name_owner_changed,
NULL);
g_assert (s1 != 0);
g_assert (s2 != 0);
g_assert (s3 != 0);
count_s1 = 0;
count_s2 = 0;
count_name_owner_changed = 0;
/*
* Bring up two other connections
*/
c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (c2 != NULL);
g_assert (!g_dbus_connection_is_closed (c2));
g_assert_cmpstr (g_dbus_connection_get_unique_name (c2), ==, ":1.2");
c3 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (c3 != NULL);
g_assert (!g_dbus_connection_is_closed (c3));
g_assert_cmpstr (g_dbus_connection_get_unique_name (c3), ==, ":1.3");
/*
* Make c2 emit "Foo" - we should catch it twice
*
* Note that there is no way to be sure that the signal subscriptions
* on c1 are effective yet - for all we know, the AddMatch() messages
* could sit waiting in a buffer somewhere between this process and
* the message bus. And emitting signals on c2 (a completely other
* socket!) will not necessarily change this.
*
* To ensure this is not the case, do a synchronous call on c1.
*/
result = g_dbus_connection_invoke_method_sync (c1,
"org.freedesktop.DBus", /* bus name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetId", /* method name */
NULL, /* parameters */
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_variant_unref (result);
/* now, emit the signal on c2 */
ret = g_dbus_connection_emit_signal (c2,
NULL, /* destination bus name */
"/org/gtk/GDBus/ExampleInterface",
"org.gtk.GDBus.ExampleInterface",
"Foo",
NULL,
&error);
g_assert_no_error (error);
g_assert (ret);
while (!(count_s1 == 1 && count_s2 == 1))
g_main_loop_run (loop);
g_assert_cmpint (count_s1, ==, 1);
g_assert_cmpint (count_s2, ==, 1);
/*
* Make c3 emit "Foo" - we should catch it only once
*/
ret = g_dbus_connection_emit_signal (c3,
NULL, /* destination bus name */
"/org/gtk/GDBus/ExampleInterface",
"org.gtk.GDBus.ExampleInterface",
"Foo",
NULL,
&error);
g_assert_no_error (error);
g_assert (ret);
while (!(count_s1 == 1 && count_s2 == 2))
g_main_loop_run (loop);
g_assert_cmpint (count_s1, ==, 1);
g_assert_cmpint (count_s2, ==, 2);
/*
* Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling
* to avoid spinning forever
*/
gboolean quit_mainloop_fired;
guint quit_mainloop_id;
quit_mainloop_fired = FALSE;
quit_mainloop_id = g_timeout_add (5000, test_connection_signal_quit_mainloop, &quit_mainloop_fired);
while (count_name_owner_changed != 2 && !quit_mainloop_fired)
g_main_loop_run (loop);
g_source_remove (quit_mainloop_id);
g_assert_cmpint (count_s1, ==, 1);
g_assert_cmpint (count_s2, ==, 2);
g_assert_cmpint (count_name_owner_changed, ==, 2);
g_dbus_connection_signal_unsubscribe (c1, s1);
g_dbus_connection_signal_unsubscribe (c1, s2);
g_dbus_connection_signal_unsubscribe (c1, s3);
_g_object_wait_for_single_ref (c1);
_g_object_wait_for_single_ref (c2);
_g_object_wait_for_single_ref (c3);
g_object_unref (c1);
g_object_unref (c2);
g_object_unref (c3);
session_bus_down ();
}
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
guint num_handled;
guint32 serial;
} FilterData;
static gboolean
filter_func (GDBusConnection *connection,
GDBusMessage *message,
gpointer user_data)
{
FilterData *data = user_data;
guint32 reply_serial;
reply_serial = g_dbus_message_get_reply_serial (message);
if (reply_serial == data->serial)
data->num_handled += 1;
return FALSE;
}
static void
test_connection_filter (void)
{
GDBusConnection *c;
FilterData data;
GDBusMessage *m;
GDBusMessage *r;
GError *error;
guint filter_id;
memset (&data, '\0', sizeof (FilterData));
session_bus_up ();
error = NULL;
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (c != NULL);
filter_id = g_dbus_connection_add_filter (c,
filter_func,
&data,
NULL);
m = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */
"/org/freedesktop/DBus", /* path */
"org.freedesktop.DBus", /* interface */
"GetNameOwner");
g_dbus_message_set_body (m, g_variant_new ("(s)", "org.freedesktop.DBus"));
error = NULL;
g_dbus_connection_send_message (c, m, &data.serial, &error);
g_assert_no_error (error);
while (data.num_handled == 0)
g_thread_yield ();
g_dbus_connection_send_message (c, m, &data.serial, &error);
g_assert_no_error (error);
while (data.num_handled == 1)
g_thread_yield ();
r = g_dbus_connection_send_message_with_reply_sync (c,
m,
-1,
&data.serial,
NULL, /* GCancellable */
&error);
g_assert_no_error (error);
g_assert (r != NULL);
g_object_unref (r);
g_assert_cmpint (data.num_handled, ==, 3);
g_dbus_connection_remove_filter (c, filter_id);
r = g_dbus_connection_send_message_with_reply_sync (c,
m,
-1,
&data.serial,
NULL, /* GCancellable */
&error);
g_assert_no_error (error);
g_assert (r != NULL);
g_object_unref (r);
g_assert_cmpint (data.num_handled, ==, 3);
_g_object_wait_for_single_ref (c);
g_object_unref (c);
g_object_unref (m);
session_bus_down ();
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
g_type_init ();
g_test_init (&argc, &argv, NULL);
/* all the tests rely on a shared main loop */
loop = g_main_loop_new (NULL, FALSE);
/* all the tests use a session bus with a well-known address that we can bring up and down
* using session_bus_up() and session_bus_down().
*/
g_unsetenv ("DISPLAY");
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
g_test_add_func ("/gdbus/connection-life-cycle", test_connection_life_cycle);
g_test_add_func ("/gdbus/connection-send", test_connection_send);
g_test_add_func ("/gdbus/connection-signals", test_connection_signals);
g_test_add_func ("/gdbus/connection-filter", test_connection_filter);
return g_test_run();
}

198
gio/tests/gdbus-error.c Normal file
View File

@ -0,0 +1,198 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include <string.h>
/* ---------------------------------------------------------------------------------------------------- */
/* Test that registered errors are properly mapped */
/* ---------------------------------------------------------------------------------------------------- */
static void
check_registered_error (const gchar *given_dbus_error_name,
GQuark error_domain,
gint error_code)
{
GError *error;
gchar *dbus_error_name;
error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message");
g_assert_error (error, error_domain, error_code);
g_assert (g_dbus_error_is_remote_error (error));
g_assert (g_dbus_error_strip_remote_error (error));
g_assert_cmpstr (error->message, ==, "test message");
dbus_error_name = g_dbus_error_get_remote_error (error);
g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name);
g_free (dbus_error_name);
g_error_free (error);
}
static void
test_registered_errors (void)
{
/* Here we check that we are able to map to GError and back for registered
* errors.
*
* For example, if "org.freedesktop.DBus.Error.AddressInUse" is
* associated with (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED), check
* that
*
* - Creating a GError for e.g. "org.freedesktop.DBus.Error.AddressInUse"
* has (error_domain, code) == (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED)
*
* - That it is possible to recover e.g. "org.freedesktop.DBus.Error.AddressInUse"
* as the D-Bus error name when dealing with an error with (error_domain, code) ==
* (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED)
*
* We just check a couple of well-known errors.
*/
check_registered_error ("org.freedesktop.DBus.Error.Failed",
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED);
check_registered_error ("org.freedesktop.DBus.Error.AddressInUse",
G_DBUS_ERROR,
G_DBUS_ERROR_ADDRESS_IN_USE);
check_registered_error ("org.freedesktop.DBus.Error.UnknownMethod",
G_DBUS_ERROR,
G_DBUS_ERROR_UNKNOWN_METHOD);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
check_unregistered_error (const gchar *given_dbus_error_name)
{
GError *error;
gchar *dbus_error_name;
error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message");
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
g_assert (g_dbus_error_is_remote_error (error));
dbus_error_name = g_dbus_error_get_remote_error (error);
g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name);
g_free (dbus_error_name);
/* strip the message */
g_assert (g_dbus_error_strip_remote_error (error));
g_assert_cmpstr (error->message, ==, "test message");
/* check that we can no longer recover the D-Bus error name */
g_assert (g_dbus_error_get_remote_error (error) == NULL);
g_error_free (error);
}
static void
test_unregistered_errors (void)
{
/* Here we check that we are able to map to GError and back for unregistered
* errors.
*
* For example, if "com.example.Error.Failed" is not registered, then check
*
* - Creating a GError for e.g. "com.example.Error.Failed" has (error_domain, code) ==
* (G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)
*
* - That it is possible to recover e.g. "com.example.Error.Failed" from that
* GError.
*
* We just check a couple of random errors.
*/
check_unregistered_error ("com.example.Error.Failed");
check_unregistered_error ("foobar.buh");
}
/* ---------------------------------------------------------------------------------------------------- */
static void
check_transparent_gerror (GQuark error_domain,
gint error_code)
{
GError *error;
gchar *given_dbus_error_name;
gchar *dbus_error_name;
error = g_error_new (error_domain, error_code, "test message");
given_dbus_error_name = g_dbus_error_encode_gerror (error);
g_assert (g_str_has_prefix (given_dbus_error_name, "org.gtk.GDBus.UnmappedGError.Quark"));
g_error_free (error);
error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message");
g_assert_error (error, error_domain, error_code);
g_assert (g_dbus_error_is_remote_error (error));
dbus_error_name = g_dbus_error_get_remote_error (error);
g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name);
g_free (dbus_error_name);
g_free (given_dbus_error_name);
/* strip the message */
g_assert (g_dbus_error_strip_remote_error (error));
g_assert_cmpstr (error->message, ==, "test message");
/* check that we can no longer recover the D-Bus error name */
g_assert (g_dbus_error_get_remote_error (error) == NULL);
g_error_free (error);
}
static void
test_transparent_gerror (void)
{
/* Here we check that we are able to transparent pass unregistered GError's
* over the wire.
*
* For example, if G_IO_ERROR_FAILED is not registered, then check
*
* - g_dbus_error_encode_gerror() returns something of the form
* org.gtk.GDBus.UnmappedGError.Quark_HEXENCODED_QUARK_NAME_.Code_ERROR_CODE
*
* - mapping back the D-Bus error name gives us G_IO_ERROR_FAILED
*
* - That it is possible to recover the D-Bus error name from the
* GError.
*
* We just check a couple of random errors.
*/
check_transparent_gerror (G_IO_ERROR, G_IO_ERROR_FAILED);
check_transparent_gerror (G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE);
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/gdbus/registered-errors", test_registered_errors);
g_test_add_func ("/gdbus/unregistered-errors", test_unregistered_errors);
g_test_add_func ("/gdbus/transparent-gerror", test_transparent_gerror);
return g_test_run();
}

View File

@ -0,0 +1,99 @@
/*
* Copyright © 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
static void
on_bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
/* This is where we'd export some objects on the bus */
}
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print ("Acquired the name %s on the session bus\n", name);
}
static void
on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print ("Lost the name %s on the session bus\n", name);
}
int
main (int argc, char *argv[])
{
guint owner_id;
GMainLoop *loop;
GBusNameOwnerFlags flags;
gboolean opt_replace;
gboolean opt_allow_replacement;
gchar *opt_name;
GOptionContext *opt_context;
GError *error;
GOptionEntry opt_entries[] =
{
{ "replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace, "Replace existing name if possible", NULL },
{ "allow-replacement", 'a', 0, G_OPTION_ARG_NONE, &opt_allow_replacement, "Allow replacement", NULL },
{ "name", 'n', 0, G_OPTION_ARG_STRING, &opt_name, "Name to acquire", NULL },
{ NULL}
};
g_type_init ();
error = NULL;
opt_name = NULL;
opt_replace = FALSE;
opt_allow_replacement = FALSE;
opt_context = g_option_context_new ("g_bus_own_name() example");
g_option_context_add_main_entries (opt_context, opt_entries, NULL);
if (!g_option_context_parse (opt_context, &argc, &argv, &error))
{
g_printerr ("Error parsing options: %s", error->message);
return 1;
}
if (opt_name == NULL)
{
g_printerr ("Incorrect usage, try --help.\n");
return 1;
}
flags = G_BUS_NAME_OWNER_FLAGS_NONE;
if (opt_replace)
flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
if (opt_allow_replacement)
flags |= G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
opt_name,
flags,
on_bus_acquired,
on_name_acquired,
on_name_lost,
NULL,
NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_bus_unown_name (owner_id);
return 0;
}

View File

@ -0,0 +1,318 @@
/*
* Copyright © 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
/*
Usage examples (modulo addresses / credentials).
UNIX domain socket transport:
Server:
$ ./gdbus-example-peer --server --address unix:abstract=myaddr
Server is listening at: unix:abstract=myaddr
Client connected.
Peer credentials: GCredentials:unix-user=500,unix-group=500,unix-process=13378
Negotiated capabilities: unix-fd-passing=1
Client said: Hey, it's 1273093080 already!
Client:
$ ./gdbus-example-peer --address unix:abstract=myaddr
Connected.
Negotiated capabilities: unix-fd-passing=1
Server said: You said 'Hey, it's 1273093080 already!'. KTHXBYE!
Nonce-secured TCP transport on the same host:
Server:
$ ./gdbus-example-peer --server --address nonce-tcp:
Server is listening at: nonce-tcp:host=localhost,port=43077,noncefile=/tmp/gdbus-nonce-file-X1ZNCV
Client connected.
Peer credentials: (no credentials received)
Negotiated capabilities: unix-fd-passing=0
Client said: Hey, it's 1273093206 already!
Client:
$ ./gdbus-example-peer -address nonce-tcp:host=localhost,port=43077,noncefile=/tmp/gdbus-nonce-file-X1ZNCV
Connected.
Negotiated capabilities: unix-fd-passing=0
Server said: You said 'Hey, it's 1273093206 already!'. KTHXBYE!
TCP transport on two different hosts with a shared home directory:
Server:
host1 $ ./gdbus-example-peer --server --address tcp:host=0.0.0.0
Server is listening at: tcp:host=0.0.0.0,port=46314
Client connected.
Peer credentials: (no credentials received)
Negotiated capabilities: unix-fd-passing=0
Client said: Hey, it's 1273093337 already!
Client:
host2 $ ./gdbus-example-peer -a tcp:host=host1,port=46314
Connected.
Negotiated capabilities: unix-fd-passing=0
Server said: You said 'Hey, it's 1273093337 already!'. KTHXBYE!
TCP transport on two different hosts without authentication:
Server:
host1 $ ./gdbus-example-peer --server --address tcp:host=0.0.0.0 --allow-anonymous
Server is listening at: tcp:host=0.0.0.0,port=59556
Client connected.
Peer credentials: (no credentials received)
Negotiated capabilities: unix-fd-passing=0
Client said: Hey, it's 1273093652 already!
Client:
host2 $ ./gdbus-example-peer -a tcp:host=host1,port=59556
Connected.
Negotiated capabilities: unix-fd-passing=0
Server said: You said 'Hey, it's 1273093652 already!'. KTHXBYE!
*/
#include <gio/gio.h>
#include <stdlib.h>
/* ---------------------------------------------------------------------------------------------------- */
static GDBusNodeInfo *introspection_data = NULL;
/* Introspection data for the service we are exporting */
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.gtk.GDBus.TestPeerInterface'>"
" <method name='HelloWorld'>"
" <arg type='s' name='greeting' direction='in'/>"
" <arg type='s' name='response' direction='out'/>"
" </method>"
" </interface>"
"</node>";
/* ---------------------------------------------------------------------------------------------------- */
static void
handle_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
if (g_strcmp0 (method_name, "HelloWorld") == 0)
{
const gchar *greeting;
gchar *response;
g_variant_get (parameters, "(s)", &greeting);
response = g_strdup_printf ("You said '%s'. KTHXBYE!", greeting);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(s)", response));
g_free (response);
g_print ("Client said: %s\n", greeting);
}
}
static const GDBusInterfaceVTable interface_vtable =
{
handle_method_call,
NULL,
NULL,
};
/* ---------------------------------------------------------------------------------------------------- */
static void
on_new_connection (GDBusServer *server,
GDBusConnection *connection,
gpointer user_data)
{
guint registration_id;
GCredentials *credentials;
gchar *s;
credentials = g_dbus_connection_get_peer_credentials (connection);
if (credentials == NULL)
s = g_strdup ("(no credentials received)");
else
s = g_credentials_to_string (credentials);
g_print ("Client connected.\n"
"Peer credentials: %s\n"
"Negotiated capabilities: unix-fd-passing=%d\n",
s,
g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
g_object_ref (connection);
registration_id = g_dbus_connection_register_object (connection,
"/org/gtk/GDBus/TestObject",
"org.gtk.GDBus.TestPeerInterface",
introspection_data->interfaces[0],
&interface_vtable,
NULL, /* user_data */
NULL, /* user_data_free_func */
NULL); /* GError** */
g_assert (registration_id > 0);
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc, char *argv[])
{
gint ret;
gboolean opt_server;
gchar *opt_address;
GOptionContext *opt_context;
gboolean opt_allow_anonymous;
GError *error;
GOptionEntry opt_entries[] =
{
{ "server", 's', 0, G_OPTION_ARG_NONE, &opt_server, "Start a server instead of a client", NULL },
{ "address", 'a', 0, G_OPTION_ARG_STRING, &opt_address, "D-Bus address to use", NULL },
{ "allow-anonymous", 'n', 0, G_OPTION_ARG_NONE, &opt_allow_anonymous, "Allow anonymous authentication", NULL },
{ NULL}
};
ret = 1;
g_type_init ();
opt_address = NULL;
opt_server = FALSE;
opt_allow_anonymous = FALSE;
opt_context = g_option_context_new ("peer-to-peer example");
error = NULL;
g_option_context_add_main_entries (opt_context, opt_entries, NULL);
if (!g_option_context_parse (opt_context, &argc, &argv, &error))
{
g_printerr ("Error parsing options: %s\n", error->message);
g_error_free (error);
goto out;
}
if (opt_address == NULL)
{
g_printerr ("Incorrect usage, try --help.\n");
goto out;
}
if (!opt_server && opt_allow_anonymous)
{
g_printerr ("The --allow-anonymous option only makes sense when used with --server.\n");
goto out;
}
/* We are lazy here - we don't want to manually provide
* the introspection data structures - so we just build
* them from XML.
*/
introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
g_assert (introspection_data != NULL);
if (opt_server)
{
GDBusServer *server;
gchar *guid;
GMainLoop *loop;
GDBusServerFlags server_flags;
guid = g_dbus_generate_guid ();
server_flags = G_DBUS_SERVER_FLAGS_NONE;
if (opt_allow_anonymous)
server_flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
error = NULL;
server = g_dbus_server_new_sync (opt_address,
server_flags,
guid,
NULL, /* GDBusAuthObserver */
NULL, /* GCancellable */
&error);
g_dbus_server_start (server);
g_free (guid);
if (server == NULL)
{
g_printerr ("Error creating server at address %s: %s\n", opt_address, error->message);
g_error_free (error);
goto out;
}
g_print ("Server is listening at: %s\n", g_dbus_server_get_client_address (server));
g_signal_connect (server,
"new-connection",
G_CALLBACK (on_new_connection),
NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_object_unref (server);
g_main_loop_unref (loop);
}
else
{
GDBusConnection *connection;
const gchar *greeting_response;
GVariant *value;
gchar *greeting;
error = NULL;
connection = g_dbus_connection_new_for_address_sync (opt_address,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
NULL, /* GCancellable */
&error);
if (connection == NULL)
{
g_printerr ("Error connecting to D-Bus address %s: %s\n", opt_address, error->message);
g_error_free (error);
goto out;
}
g_print ("Connected.\n"
"Negotiated capabilities: unix-fd-passing=%d\n",
g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
greeting = g_strdup_printf ("Hey, it's %" G_GUINT64_FORMAT " already!", (guint64) time (NULL));
value = g_dbus_connection_invoke_method_sync (connection,
NULL, /* bus_name */
"/org/gtk/GDBus/TestObject",
"org.gtk.GDBus.TestPeerInterface",
"HelloWorld",
g_variant_new ("(s)", greeting),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
if (value == NULL)
{
g_printerr ("Error invoking HelloWorld(): %s\n", error->message);
g_error_free (error);
goto out;
}
g_variant_get (value, "(s)", &greeting_response);
g_print ("Server said: %s\n", greeting_response);
g_variant_unref (value);
g_object_unref (connection);
}
g_dbus_node_info_unref (introspection_data);
ret = 0;
out:
return ret;
}

View File

@ -0,0 +1,388 @@
/*
* Copyright © 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <stdlib.h>
#ifdef G_OS_UNIX
/* For STDOUT_FILENO */
#include <unistd.h>
#endif
/* ---------------------------------------------------------------------------------------------------- */
static GDBusNodeInfo *introspection_data = NULL;
/* Introspection data for the service we are exporting */
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.gtk.GDBus.TestInterface'>"
" <method name='HelloWorld'>"
" <arg type='s' name='greeting' direction='in'/>"
" <arg type='s' name='response' direction='out'/>"
" </method>"
" <method name='EmitSignal'>"
" <arg type='d' name='speed_in_mph' direction='in'/>"
" </method>"
" <method name='GimmeStdout'/>"
" <signal name='VelocityChanged'>"
" <arg type='d' name='speed_in_mph'/>"
" <arg type='s' name='speed_as_string'/>"
" </signal>"
" <property type='s' name='FluxCapicitorName' access='read'/>"
" <property type='s' name='Title' access='readwrite'/>"
" <property type='s' name='ReadingAlwaysThrowsError' access='read'/>"
" <property type='s' name='WritingAlwaysThrowsError' access='readwrite'/>"
" <property type='s' name='OnlyWritable' access='write'/>"
" <property type='s' name='Foo' access='read'/>"
" <property type='s' name='Bar' access='read'/>"
" </interface>"
"</node>";
/* ---------------------------------------------------------------------------------------------------- */
static void
handle_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
if (g_strcmp0 (method_name, "HelloWorld") == 0)
{
const gchar *greeting;
g_variant_get (parameters, "(s)", &greeting);
if (g_strcmp0 (greeting, "Return Unregistered") == 0)
{
g_dbus_method_invocation_return_error (invocation,
G_IO_ERROR,
G_IO_ERROR_FAILED_HANDLED,
"As requested, here's a GError not registered (G_IO_ERROR_FAILED_HANDLED)");
}
else if (g_strcmp0 (greeting, "Return Registered") == 0)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,
"As requested, here's a GError that is registered (G_DBUS_ERROR_MATCH_RULE_NOT_FOUND)");
}
else if (g_strcmp0 (greeting, "Return Raw") == 0)
{
g_dbus_method_invocation_return_dbus_error (invocation,
"org.gtk.GDBus.SomeErrorName",
"As requested, here's a raw D-Bus error");
}
else
{
gchar *response;
response = g_strdup_printf ("You greeted me with '%s'. Thanks!", greeting);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(s)", response));
g_free (response);
}
}
else if (g_strcmp0 (method_name, "EmitSignal") == 0)
{
GError *local_error;
gdouble speed_in_mph;
gchar *speed_as_string;
g_variant_get (parameters, "(d)", &speed_in_mph);
speed_as_string = g_strdup_printf ("%g mph!", speed_in_mph);
local_error = NULL;
g_dbus_connection_emit_signal (connection,
NULL,
object_path,
interface_name,
"VelocityChanged",
g_variant_new ("(ds)",
speed_in_mph,
speed_as_string),
&local_error);
g_assert_no_error (local_error);
g_free (speed_as_string);
g_dbus_method_invocation_return_value (invocation, NULL);
}
else if (g_strcmp0 (method_name, "GimmeStdout") == 0)
{
#ifdef G_OS_UNIX
if (g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)
{
GDBusMessage *reply;
GUnixFDList *fd_list;
GError *error;
fd_list = g_unix_fd_list_new ();
error = NULL;
g_unix_fd_list_append (fd_list, STDOUT_FILENO, &error);
g_assert_no_error (error);
reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation));
g_dbus_message_set_unix_fd_list (reply, fd_list);
error = NULL;
g_dbus_connection_send_message (connection,
reply,
NULL, /* out_serial */
&error);
g_assert_no_error (error);
g_object_unref (invocation);
g_object_unref (fd_list);
g_object_unref (reply);
}
else
{
g_dbus_method_invocation_return_dbus_error (invocation,
"org.gtk.GDBus.Failed",
"Your message bus daemon does not support file descriptor passing (need D-Bus >= 1.3.0)");
}
#else
g_dbus_method_invocation_return_dbus_error (invocation,
"org.gtk.GDBus.NotOnUnix",
"Your OS does not support file descriptor passing");
#endif
}
}
static gchar *_global_title = NULL;
static gboolean swap_a_and_b = FALSE;
static GVariant *
handle_get_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
GVariant *ret;
ret = NULL;
if (g_strcmp0 (property_name, "FluxCapicitorName") == 0)
{
ret = g_variant_new_string ("DeLorean");
}
else if (g_strcmp0 (property_name, "Title") == 0)
{
if (_global_title == NULL)
_global_title = g_strdup ("Back To C!");
ret = g_variant_new_string (_global_title);
}
else if (g_strcmp0 (property_name, "ReadingAlwaysThrowsError") == 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Hello %s. I thought I said reading this property "
"always results in an error. kthxbye",
sender);
}
else if (g_strcmp0 (property_name, "WritingAlwaysThrowsError") == 0)
{
ret = g_variant_new_string ("There's no home like home");
}
else if (g_strcmp0 (property_name, "Foo") == 0)
{
ret = g_variant_new_string (swap_a_and_b ? "Tock" : "Tick");
}
else if (g_strcmp0 (property_name, "Bar") == 0)
{
ret = g_variant_new_string (swap_a_and_b ? "Tick" : "Tock");
}
return ret;
}
static gboolean
handle_set_property (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 (g_strcmp0 (property_name, "Title") == 0)
{
if (g_strcmp0 (_global_title, g_variant_get_string (value, NULL)) != 0)
{
GVariantBuilder *builder;
GError *local_error;
g_free (_global_title);
_global_title = g_variant_dup_string (value, NULL);
local_error = NULL;
builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
g_variant_builder_add (builder,
"{sv}",
"Title",
g_variant_new_string (_global_title));
g_dbus_connection_emit_signal (connection,
NULL,
object_path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
g_variant_new ("(sa{sv})",
interface_name,
builder),
&local_error);
g_assert_no_error (local_error);
}
}
else if (g_strcmp0 (property_name, "ReadingAlwaysThrowsError") == 0)
{
/* do nothing - they can't read it after all! */
}
else if (g_strcmp0 (property_name, "WritingAlwaysThrowsError") == 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Hello AGAIN %s. I thought I said writing this property "
"always results in an error. kthxbye",
sender);
}
return *error == NULL;
}
/* for now */
static const GDBusInterfaceVTable interface_vtable =
{
handle_method_call,
handle_get_property,
handle_set_property
};
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
on_timeout_cb (gpointer user_data)
{
GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
GVariantBuilder *builder;
GError *error;
swap_a_and_b = !swap_a_and_b;
error = NULL;
builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
g_variant_builder_add (builder,
"{sv}",
"Foo",
g_variant_new_string (swap_a_and_b ? "Tock" : "Tick"));
g_variant_builder_add (builder,
"{sv}",
"Bar",
g_variant_new_string (swap_a_and_b ? "Tick" : "Tock"));
g_dbus_connection_emit_signal (connection,
NULL,
"/org/gtk/GDBus/TestObject",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
g_variant_new ("(sa{sv})",
"org.gtk.GDBus.TestInterface",
builder),
&error);
g_assert_no_error (error);
return TRUE;
}
/* ---------------------------------------------------------------------------------------------------- */
static void
on_bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
guint registration_id;
registration_id = g_dbus_connection_register_object (connection,
"/org/gtk/GDBus/TestObject",
"org.gtk.GDBus.TestInterface",
introspection_data->interfaces[0],
&interface_vtable,
NULL, /* user_data */
NULL, /* user_data_free_func */
NULL); /* GError** */
g_assert (registration_id > 0);
/* swap value of properties Foo and Bar every two seconds */
g_timeout_add_seconds (2,
on_timeout_cb,
connection);
}
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
}
static void
on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
exit (1);
}
int
main (int argc, char *argv[])
{
guint owner_id;
GMainLoop *loop;
g_type_init ();
/* We are lazy here - we don't want to manually provide
* the introspection data structures - so we just build
* them from XML.
*/
introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
g_assert (introspection_data != NULL);
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
"org.gtk.GDBus.TestServer",
G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired,
on_name_acquired,
on_name_lost,
NULL,
NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_bus_unown_name (owner_id);
g_dbus_node_info_unref (introspection_data);
return 0;
}

View File

@ -0,0 +1,410 @@
/*
* Copyright © 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <stdlib.h>
#include <string.h>
/* ---------------------------------------------------------------------------------------------------- */
static GDBusNodeInfo *introspection_data = NULL;
static const GDBusInterfaceInfo *manager_interface_info = NULL;
static const GDBusInterfaceInfo *block_interface_info = NULL;
static const GDBusInterfaceInfo *partition_interface_info = NULL;
/* Introspection data for the service we are exporting */
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.gtk.GDBus.Example.Manager'>"
" <method name='Hello'>"
" <arg type='s' name='greeting' direction='in'/>"
" <arg type='s' name='response' direction='out'/>"
" </method>"
" </interface>"
" <interface name='org.gtk.GDBus.Example.Block'>"
" <method name='Hello'>"
" <arg type='s' name='greeting' direction='in'/>"
" <arg type='s' name='response' direction='out'/>"
" </method>"
" <property type='i' name='Major' access='read'/>"
" <property type='i' name='Minor' access='read'/>"
" <property type='s' name='Notes' access='readwrite'/>"
" </interface>"
" <interface name='org.gtk.GDBus.Example.Partition'>"
" <method name='Hello'>"
" <arg type='s' name='greeting' direction='in'/>"
" <arg type='s' name='response' direction='out'/>"
" </method>"
" <property type='i' name='PartitionNumber' access='read'/>"
" <property type='s' name='Notes' access='readwrite'/>"
" </interface>"
"</node>";
/* ---------------------------------------------------------------------------------------------------- */
static void
manager_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
const gchar *greeting;
gchar *response;
g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.Example.Manager");
g_assert_cmpstr (method_name, ==, "Hello");
g_variant_get (parameters, "(s)", &greeting);
response = g_strdup_printf ("Method %s.%s with user_data `%s' on object path %s called with arg '%s'",
interface_name,
method_name,
(const gchar *) user_data,
object_path,
greeting);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(s)", response));
g_free (response);
}
const GDBusInterfaceVTable manager_vtable =
{
manager_method_call,
NULL, /* get_property */
NULL /* set_property */
};
/* ---------------------------------------------------------------------------------------------------- */
static void
block_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.Example.Block");
if (g_strcmp0 (method_name, "Hello") == 0)
{
const gchar *greeting;
gchar *response;
g_variant_get (parameters, "(s)", &greeting);
response = g_strdup_printf ("Method %s.%s with user_data `%s' on object path %s called with arg '%s'",
interface_name,
method_name,
(const gchar *) user_data,
object_path,
greeting);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(s)", response));
g_free (response);
}
else if (g_strcmp0 (method_name, "DoStuff") == 0)
{
g_dbus_method_invocation_return_dbus_error (invocation,
"org.gtk.GDBus.TestSubtree.Error.Failed",
"This method intentionally always fails");
}
else
{
g_assert_not_reached ();
}
}
static GVariant *
block_get_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
GVariant *ret;
const gchar *node;
gint major;
gint minor;
node = strrchr (object_path, '/') + 1;
if (g_str_has_prefix (node, "sda"))
major = 8;
else
major = 9;
if (strlen (node) == 4)
minor = node[3] - '0';
else
minor = 0;
ret = NULL;
if (g_strcmp0 (property_name, "Major") == 0)
{
ret = g_variant_new_int32 (major);
}
else if (g_strcmp0 (property_name, "Minor") == 0)
{
ret = g_variant_new_int32 (minor);
}
else if (g_strcmp0 (property_name, "Notes") == 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Hello %s. I thought I said reading this property "
"always results in an error. kthxbye",
sender);
}
else
{
g_assert_not_reached ();
}
return ret;
}
static gboolean
block_set_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error,
gpointer user_data)
{
/* TODO */
g_assert_not_reached ();
}
const GDBusInterfaceVTable block_vtable =
{
block_method_call,
block_get_property,
block_set_property,
};
/* ---------------------------------------------------------------------------------------------------- */
static void
partition_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
const gchar *greeting;
gchar *response;
g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.Example.Partition");
g_assert_cmpstr (method_name, ==, "Hello");
g_variant_get (parameters, "(s)", &greeting);
response = g_strdup_printf ("Method %s.%s with user_data `%s' on object path %s called with arg '%s'",
interface_name,
method_name,
(const gchar *) user_data,
object_path,
greeting);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(s)", response));
g_free (response);
}
const GDBusInterfaceVTable partition_vtable =
{
partition_method_call,
//partition_get_property,
//partition_set_property
};
/* ---------------------------------------------------------------------------------------------------- */
static gchar **
subtree_enumerate (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
gpointer user_data)
{
gchar **nodes;
GPtrArray *p;
p = g_ptr_array_new ();
g_ptr_array_add (p, g_strdup ("sda"));
g_ptr_array_add (p, g_strdup ("sda1"));
g_ptr_array_add (p, g_strdup ("sda2"));
g_ptr_array_add (p, g_strdup ("sda3"));
g_ptr_array_add (p, g_strdup ("sdb"));
g_ptr_array_add (p, g_strdup ("sdb1"));
g_ptr_array_add (p, g_strdup ("sdc"));
g_ptr_array_add (p, g_strdup ("sdc1"));
g_ptr_array_add (p, NULL);
nodes = (gchar **) g_ptr_array_free (p, FALSE);
return nodes;
}
static GPtrArray *
subtree_introspect (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *node,
gpointer user_data)
{
GPtrArray *p;
p = g_ptr_array_new ();
if (g_strcmp0 (node, "/") == 0)
{
g_ptr_array_add (p, (gpointer) manager_interface_info);
}
else
{
g_ptr_array_add (p, (gpointer) block_interface_info);
if (strlen (node) == 4)
g_ptr_array_add (p, (gpointer) partition_interface_info);
}
return p;
}
static const GDBusInterfaceVTable *
subtree_dispatch (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *node,
gpointer *out_user_data,
gpointer user_data)
{
const GDBusInterfaceVTable *vtable_to_return;
gpointer user_data_to_return;
if (g_strcmp0 (interface_name, "org.gtk.GDBus.Example.Manager") == 0)
{
user_data_to_return = "The Root";
vtable_to_return = &manager_vtable;
}
else
{
if (strlen (node) == 4)
user_data_to_return = "A partition";
else
user_data_to_return = "A block device";
if (g_strcmp0 (interface_name, "org.gtk.GDBus.Example.Block") == 0)
vtable_to_return = &block_vtable;
else if (g_strcmp0 (interface_name, "org.gtk.GDBus.Example.Partition") == 0)
vtable_to_return = &partition_vtable;
else
g_assert_not_reached ();
}
*out_user_data = user_data_to_return;
return vtable_to_return;
}
const GDBusSubtreeVTable subtree_vtable =
{
subtree_enumerate,
subtree_introspect,
subtree_dispatch
};
/* ---------------------------------------------------------------------------------------------------- */
static void
on_bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
guint registration_id;
registration_id = g_dbus_connection_register_subtree (connection,
"/org/gtk/GDBus/TestSubtree/Devices",
&subtree_vtable,
G_DBUS_SUBTREE_FLAGS_NONE,
NULL, /* user_data */
NULL, /* user_data_free_func */
NULL); /* GError** */
g_assert (registration_id > 0);
}
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
}
static void
on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
exit (1);
}
int
main (int argc, char *argv[])
{
guint owner_id;
GMainLoop *loop;
g_type_init ();
/* We are lazy here - we don't want to manually provide
* the introspection data structures - so we just build
* them from XML.
*/
introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
g_assert (introspection_data != NULL);
manager_interface_info = g_dbus_node_info_lookup_interface (introspection_data, "org.gtk.GDBus.Example.Manager");
block_interface_info = g_dbus_node_info_lookup_interface (introspection_data, "org.gtk.GDBus.Example.Block");
partition_interface_info = g_dbus_node_info_lookup_interface (introspection_data, "org.gtk.GDBus.Example.Partition");
g_assert (manager_interface_info != NULL);
g_assert (block_interface_info != NULL);
g_assert (partition_interface_info != NULL);
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
"org.gtk.GDBus.TestSubtree",
G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired,
on_name_acquired,
on_name_lost,
NULL,
NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_bus_unown_name (owner_id);
g_dbus_node_info_unref (introspection_data);
return 0;
}

View File

@ -0,0 +1,145 @@
/*
* Copyright © 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <gio/gio.h>
/* see gdbus-example-server.c for the server implementation */
static gint
get_server_stdout (GDBusConnection *connection,
const gchar *name_owner,
GError **error)
{
GDBusMessage *method_call_message;
GDBusMessage *method_reply_message;
GUnixFDList *fd_list;
gint fd;
fd = -1;
method_call_message = NULL;
method_reply_message = NULL;
method_call_message = g_dbus_message_new_method_call (name_owner,
"/org/gtk/GDBus/TestObject",
"org.gtk.GDBus.TestInterface",
"GimmeStdout");
method_reply_message = g_dbus_connection_send_message_with_reply_sync (connection,
method_call_message,
-1,
NULL, /* out_serial */
NULL, /* cancellable */
error);
if (method_reply_message == NULL)
goto out;
if (g_dbus_message_get_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_ERROR)
{
g_dbus_message_to_gerror (method_reply_message, error);
goto out;
}
fd_list = g_dbus_message_get_unix_fd_list (method_reply_message);
fd = g_unix_fd_list_get (fd_list, 0, error);
out:
g_object_unref (method_call_message);
g_object_unref (method_reply_message);
return fd;
}
static void
on_name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
gint fd;
GError *error;
error = NULL;
fd = get_server_stdout (connection, name_owner, &error);
if (fd == -1)
{
g_printerr ("Error invoking GimmeStdout(): %s\n",
error->message);
g_error_free (error);
exit (1);
}
else
{
gchar now_buf[256];
time_t now;
gssize len;
gchar *str;
now = time (NULL);
strftime (now_buf,
sizeof now_buf,
"%c",
localtime (&now));
str = g_strdup_printf ("On %s, gdbus-example-unix-fd-client with pid %d was here!\n",
now_buf,
(gint) getpid ());
len = strlen (str);
g_warn_if_fail (write (fd, str, len) == len);
close (fd);
g_print ("Wrote the following on server's stdout:\n%s", str);
g_free (str);
exit (0);
}
}
static void
on_name_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_printerr ("Failed to get name owner for %s\n"
"Is ./gdbus-example-server running?\n",
name);
exit (1);
}
int
main (int argc, char *argv[])
{
guint watcher_id;
GMainLoop *loop;
g_type_init ();
watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.GDBus.TestServer",
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_name_appeared,
on_name_vanished,
NULL,
NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_bus_unwatch_name (watcher_id);
return 0;
}

View File

@ -0,0 +1,101 @@
/*
* Copyright © 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
static gchar *opt_name = NULL;
static gboolean opt_system_bus = FALSE;
static gboolean opt_auto_start = FALSE;
static GOptionEntry opt_entries[] =
{
{ "name", 'n', 0, G_OPTION_ARG_STRING, &opt_name, "Name to watch", NULL },
{ "system-bus", 's', 0, G_OPTION_ARG_NONE, &opt_system_bus, "Use the system-bus instead of the session-bus", NULL },
{ "auto-start", 'a', 0, G_OPTION_ARG_NONE, &opt_auto_start, "Instruct the bus to launch an owner for the name", NULL},
{ NULL}
};
static void
on_name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
g_print ("Name %s on %s is owned by %s\n",
name,
opt_system_bus ? "the system bus" : "the session bus",
name_owner);
}
static void
on_name_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print ("Name %s does not exist on %s\n",
name,
opt_system_bus ? "the system bus" : "the session bus");
}
int
main (int argc, char *argv[])
{
guint watcher_id;
GMainLoop *loop;
GOptionContext *opt_context;
GError *error;
GBusNameWatcherFlags flags;
g_type_init ();
error = NULL;
opt_context = g_option_context_new ("g_bus_watch_name() example");
g_option_context_set_summary (opt_context,
"Example: to watch the power manager on the session bus, use:\n"
"\n"
" ./example-watch-name -n org.gnome.PowerManager");
g_option_context_add_main_entries (opt_context, opt_entries, NULL);
if (!g_option_context_parse (opt_context, &argc, &argv, &error))
{
g_printerr ("Error parsing options: %s", error->message);
goto out;
}
if (opt_name == NULL)
{
g_printerr ("Incorrect usage, try --help.\n");
goto out;
}
flags = G_BUS_NAME_WATCHER_FLAGS_NONE;
if (opt_auto_start)
flags |= G_BUS_NAME_WATCHER_FLAGS_AUTO_START;
watcher_id = g_bus_watch_name (opt_system_bus ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION,
opt_name,
flags,
on_name_appeared,
on_name_vanished,
NULL,
NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_bus_unwatch_name (watcher_id);
out:
g_option_context_free (opt_context);
g_free (opt_name);
return 0;
}

View File

@ -0,0 +1,205 @@
/*
* Copyright © 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
static gchar *opt_name = NULL;
static gchar *opt_object_path = NULL;
static gchar *opt_interface = NULL;
static gboolean opt_system_bus = FALSE;
static gboolean opt_auto_start = FALSE;
static gboolean opt_no_properties = FALSE;
static GOptionEntry opt_entries[] =
{
{ "name", 'n', 0, G_OPTION_ARG_STRING, &opt_name, "Name of the remote object to watch", NULL },
{ "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_object_path, "Object path of the remote object", NULL },
{ "interface", 'i', 0, G_OPTION_ARG_STRING, &opt_interface, "D-Bus interface of remote object", NULL },
{ "system-bus", 's', 0, G_OPTION_ARG_NONE, &opt_system_bus, "Use the system-bus instead of the session-bus", NULL },
{ "auto-start", 'a', 0, G_OPTION_ARG_NONE, &opt_auto_start, "Instruct the bus to launch an owner for the name", NULL},
{ "no-properties", 'p', 0, G_OPTION_ARG_NONE, &opt_no_properties, "Do not load properties", NULL},
{ NULL}
};
static void
print_properties (GDBusProxy *proxy)
{
gchar **property_names;
guint n;
g_print (" properties:\n");
property_names = g_dbus_proxy_get_cached_property_names (proxy, NULL);
for (n = 0; property_names != NULL && property_names[n] != NULL; n++)
{
const gchar *key = property_names[n];
GVariant *value;
gchar *value_str;
value = g_dbus_proxy_get_cached_property (proxy, key, NULL);
value_str = g_variant_print (value, TRUE);
g_print (" %s -> %s\n", key, value_str);
g_variant_unref (value);
g_free (value_str);
}
g_strfreev (property_names);
}
static void
on_properties_changed (GDBusProxy *proxy,
GHashTable *changed_properties,
gpointer user_data)
{
GHashTableIter iter;
const gchar *key;
GVariant *value;
g_print (" *** Properties Changed:\n");
g_hash_table_iter_init (&iter, changed_properties);
while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value))
{
gchar *value_str;
value_str = g_variant_print (value, TRUE);
g_print (" %s -> %s\n", key, value_str);
g_free (value_str);
}
}
static void
on_signal (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
gchar *parameters_str;
parameters_str = g_variant_print (parameters, TRUE);
g_print (" *** Received Signal: %s: %s\n",
signal_name,
parameters_str);
g_free (parameters_str);
}
static void
on_proxy_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy,
gpointer user_data)
{
g_print ("+++ Acquired proxy object for remote object owned by %s\n"
" bus: %s\n"
" name: %s\n"
" object path: %s\n"
" interface: %s\n",
name_owner,
opt_system_bus ? "System Bus" : "Session Bus",
opt_name,
opt_object_path,
opt_interface);
print_properties (proxy);
g_signal_connect (proxy,
"g-properties-changed",
G_CALLBACK (on_properties_changed),
NULL);
g_signal_connect (proxy,
"g-signal",
G_CALLBACK (on_signal),
NULL);
}
static void
on_proxy_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print ("--- Cannot create proxy object for\n"
" bus: %s\n"
" name: %s\n"
" object path: %s\n"
" interface: %s\n",
opt_system_bus ? "System Bus" : "Session Bus",
opt_name,
opt_object_path,
opt_interface);
}
int
main (int argc, char *argv[])
{
guint watcher_id;
GMainLoop *loop;
GOptionContext *opt_context;
GError *error;
GBusNameWatcherFlags flags;
GDBusProxyFlags proxy_flags;
g_type_init ();
opt_context = g_option_context_new ("g_bus_watch_proxy() example");
g_option_context_set_summary (opt_context,
"Example: to watch the object of gdbus-example-server, use:\n"
"\n"
" ./gdbus-example-watch-proxy -n org.gtk.GDBus.TestServer \\\n"
" -o /org/gtk/GDBus/TestObject \\\n"
" -i org.gtk.GDBus.TestInterface");
g_option_context_add_main_entries (opt_context, opt_entries, NULL);
error = NULL;
if (!g_option_context_parse (opt_context, &argc, &argv, &error))
{
g_printerr ("Error parsing options: %s", error->message);
goto out;
}
if (opt_name == NULL || opt_object_path == NULL || opt_interface == NULL)
{
g_printerr ("Incorrect usage, try --help.\n");
goto out;
}
flags = G_BUS_NAME_WATCHER_FLAGS_NONE;
if (opt_auto_start)
flags |= G_BUS_NAME_WATCHER_FLAGS_AUTO_START;
proxy_flags = G_DBUS_PROXY_FLAGS_NONE;
if (opt_no_properties)
proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES;
watcher_id = g_bus_watch_proxy (opt_system_bus ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION,
opt_name,
flags,
opt_object_path,
opt_interface,
G_TYPE_DBUS_PROXY,
proxy_flags,
on_proxy_appeared,
on_proxy_vanished,
NULL,
NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_bus_unwatch_proxy (watcher_id);
out:
g_option_context_free (opt_context);
g_free (opt_name);
g_free (opt_object_path);
g_free (opt_interface);
return 0;
}

View File

@ -0,0 +1,82 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include <string.h>
#include "gdbus-tests.h"
/* all tests rely on a shared mainloop */
static GMainLoop *loop = NULL;
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
nuke_session_bus_cb (gpointer data)
{
g_main_loop_quit (loop);
return FALSE;
}
static void
test_exit_on_close (void)
{
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
GDBusConnection *c;
session_bus_up ();
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (c != NULL);
g_assert (!g_dbus_connection_is_closed (c));
g_timeout_add (50,
nuke_session_bus_cb,
NULL);
g_main_loop_run (loop);
session_bus_down ();
g_main_loop_run (loop);
}
g_test_trap_assert_stdout ("*Remote peer vanished. Exiting.*");
g_test_trap_assert_failed();
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
g_type_init ();
g_test_init (&argc, &argv, NULL);
/* all the tests rely on a shared main loop */
loop = g_main_loop_new (NULL, FALSE);
/* all the tests use a session bus with a well-known address that we can bring up and down
* using session_bus_up() and session_bus_down().
*/
g_unsetenv ("DISPLAY");
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
g_test_add_func ("/gdbus/exit-on-close", test_exit_on_close);
return g_test_run();
}

1410
gio/tests/gdbus-export.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include <string.h>
#include "gdbus-tests.h"
/* all tests rely on a shared mainloop */
static GMainLoop *loop = NULL;
/* ---------------------------------------------------------------------------------------------------- */
/* Test introspection parser */
/* ---------------------------------------------------------------------------------------------------- */
static void
introspection_on_proxy_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy,
gpointer user_data)
{
GError *error;
const gchar *xml_data;
GDBusNodeInfo *node_info;
const GDBusInterfaceInfo *interface_info;
const GDBusMethodInfo *method_info;
const GDBusSignalInfo *signal_info;
GVariant *result;
error = NULL;
/*
* Invoke Introspect(), then parse the output.
*/
result = g_dbus_proxy_invoke_method_sync (proxy,
"org.freedesktop.DBus.Introspectable.Introspect",
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_variant_get (result, "(s)", &xml_data);
node_info = g_dbus_node_info_new_for_xml (xml_data, &error);
g_assert_no_error (error);
g_assert (node_info != NULL);
/* for now we only check a couple of things. TODO: check more things */
interface_info = g_dbus_node_info_lookup_interface (node_info, "com.example.NonExistantInterface");
g_assert (interface_info == NULL);
interface_info = g_dbus_node_info_lookup_interface (node_info, "org.freedesktop.DBus.Introspectable");
g_assert (interface_info != NULL);
method_info = g_dbus_interface_info_lookup_method (interface_info, "NonExistantMethod");
g_assert (method_info == NULL);
method_info = g_dbus_interface_info_lookup_method (interface_info, "Introspect");
g_assert (method_info != NULL);
g_assert (method_info->in_args == NULL);
g_assert (method_info->out_args != NULL);
g_assert (method_info->out_args[0] != NULL);
g_assert (method_info->out_args[1] == NULL);
g_assert_cmpstr (method_info->out_args[0]->signature, ==, "s");
interface_info = g_dbus_node_info_lookup_interface (node_info, "com.example.Frob");
g_assert (interface_info != NULL);
signal_info = g_dbus_interface_info_lookup_signal (interface_info, "TestSignal");
g_assert (signal_info != NULL);
g_assert (signal_info->args != NULL);
g_assert (signal_info->args[0] != NULL);
g_assert_cmpstr (signal_info->args[0]->signature, ==, "s");
g_assert (signal_info->args[1] != NULL);
g_assert_cmpstr (signal_info->args[1]->signature, ==, "o");
g_assert (signal_info->args[2] != NULL);
g_assert_cmpstr (signal_info->args[2]->signature, ==, "v");
g_assert (signal_info->args[3] == NULL);
g_dbus_node_info_unref (node_info);
g_variant_unref (result);
g_main_loop_quit (loop);
}
static void
introspection_on_proxy_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
}
static void
test_introspection_parser (void)
{
guint watcher_id;
session_bus_up ();
watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION,
"com.example.TestService",
G_BUS_NAME_WATCHER_FLAGS_NONE,
"/com/example/TestObject",
"com.example.Frob",
G_TYPE_DBUS_PROXY,
G_DBUS_PROXY_FLAGS_NONE,
introspection_on_proxy_appeared,
introspection_on_proxy_vanished,
NULL,
NULL);
/* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
* until one can connect to the bus but that's not how things work right now
*/
usleep (500 * 1000);
/* this is safe; testserver will exit once the bus goes away */
g_assert (g_spawn_command_line_async ("./gdbus-testserver.py", NULL));
g_main_loop_run (loop);
g_bus_unwatch_proxy (watcher_id);
/* tear down bus */
session_bus_down ();
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
g_type_init ();
g_test_init (&argc, &argv, NULL);
/* all the tests rely on a shared main loop */
loop = g_main_loop_new (NULL, FALSE);
/* all the tests use a session bus with a well-known address that we can bring up and down
* using session_bus_up() and session_bus_down().
*/
g_unsetenv ("DISPLAY");
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
g_test_add_func ("/gdbus/introspection-parser", test_introspection_parser);
return g_test_run();
}

749
gio/tests/gdbus-names.c Normal file
View File

@ -0,0 +1,749 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include "gdbus-tests.h"
/* all tests rely on a shared mainloop */
static GMainLoop *loop;
/* ---------------------------------------------------------------------------------------------------- */
/* Test that g_bus_own_name() works correctly */
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
GMainLoop *loop;
gboolean expect_null_connection;
guint num_bus_acquired;
guint num_acquired;
guint num_lost;
guint num_free_func;
} OwnNameData;
static void
own_name_data_free_func (OwnNameData *data)
{
data->num_free_func++;
g_main_loop_quit (loop);
}
static void
bus_acquired_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
OwnNameData *data = user_data;
g_dbus_connection_set_exit_on_close (connection, FALSE);
data->num_bus_acquired += 1;
g_main_loop_quit (loop);
}
static void
name_acquired_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
OwnNameData *data = user_data;
data->num_acquired += 1;
g_main_loop_quit (loop);
}
static void
name_lost_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
OwnNameData *data = user_data;
if (data->expect_null_connection)
{
g_assert (connection == NULL);
}
else
{
g_assert (connection != NULL);
g_dbus_connection_set_exit_on_close (connection, FALSE);
}
data->num_lost += 1;
g_main_loop_quit (loop);
}
static void
test_bus_own_name (void)
{
guint id;
guint id2;
OwnNameData data;
OwnNameData data2;
const gchar *name;
GDBusConnection *c;
GError *error;
gboolean name_has_owner_reply;
GDBusConnection *c2;
GVariant *result;
error = NULL;
name = "org.gtk.GDBus.Name1";
/*
* First check that name_lost_handler() is invoked if there is no bus.
*
* Also make sure name_lost_handler() isn't invoked when unowning the name.
*/
data.num_bus_acquired = 0;
data.num_free_func = 0;
data.num_acquired = 0;
data.num_lost = 0;
data.expect_null_connection = TRUE;
id = g_bus_own_name (G_BUS_TYPE_SESSION,
name,
G_BUS_NAME_OWNER_FLAGS_NONE,
bus_acquired_handler,
name_acquired_handler,
name_lost_handler,
&data,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data.num_bus_acquired, ==, 0);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_bus_acquired, ==, 0);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 1);
g_bus_unown_name (id);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 1);
g_assert_cmpint (data.num_free_func, ==, 1);
/*
* Bring up a bus, then own a name and check bus_acquired_handler() then name_acquired_handler() is invoked.
*/
session_bus_up ();
data.num_bus_acquired = 0;
data.num_acquired = 0;
data.num_lost = 0;
data.expect_null_connection = FALSE;
id = g_bus_own_name (G_BUS_TYPE_SESSION,
name,
G_BUS_NAME_OWNER_FLAGS_NONE,
bus_acquired_handler,
name_acquired_handler,
name_lost_handler,
&data,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data.num_bus_acquired, ==, 0);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_bus_acquired, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_bus_acquired, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_lost, ==, 0);
/*
* Check that the name was actually acquired.
*/
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (c != NULL);
g_assert (!g_dbus_connection_is_closed (c));
result = g_dbus_connection_invoke_method_sync (c,
"org.freedesktop.DBus", /* bus name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"NameHasOwner", /* method name */
g_variant_new ("(s)", name),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_variant_get (result, "(b)", &name_has_owner_reply);
g_assert (name_has_owner_reply);
g_variant_unref (result);
/*
* Stop owning the name - this should invoke our free func
*/
g_bus_unown_name (id);
g_assert_cmpint (data.num_free_func, ==, 2);
/*
* Check that the name was actually released.
*/
result = g_dbus_connection_invoke_method_sync (c,
"org.freedesktop.DBus", /* bus name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"NameHasOwner", /* method name */
g_variant_new ("(s)", name),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_variant_get (result, "(b)", &name_has_owner_reply);
g_assert (!name_has_owner_reply);
g_variant_unref (result);
/*
* Own the name again.
*/
data.num_bus_acquired = 0;
data.num_acquired = 0;
data.num_lost = 0;
data.expect_null_connection = FALSE;
id = g_bus_own_name (G_BUS_TYPE_SESSION,
name,
G_BUS_NAME_OWNER_FLAGS_NONE,
bus_acquired_handler,
name_acquired_handler,
name_lost_handler,
&data,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data.num_bus_acquired, ==, 0);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_bus_acquired, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_bus_acquired, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_lost, ==, 0);
/*
* Try owning the name with another object on the same connection - this should
* fail because we already own the name.
*/
data2.num_free_func = 0;
data2.num_bus_acquired = 0;
data2.num_acquired = 0;
data2.num_lost = 0;
data2.expect_null_connection = FALSE;
id2 = g_bus_own_name (G_BUS_TYPE_SESSION,
name,
G_BUS_NAME_OWNER_FLAGS_NONE,
bus_acquired_handler,
name_acquired_handler,
name_lost_handler,
&data2,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data2.num_bus_acquired, ==, 1);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data2.num_bus_acquired, ==, 1);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_bus_unown_name (id2);
g_assert_cmpint (data2.num_bus_acquired, ==, 1);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_assert_cmpint (data2.num_free_func, ==, 1);
/*
* Create a secondary (e.g. private) connection and try owning the name on that
* connection. This should fail both with and without _REPLACE because we
* didn't specify ALLOW_REPLACEMENT.
*/
c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (c2 != NULL);
g_assert (!g_dbus_connection_is_closed (c2));
/* first without _REPLACE */
data2.num_bus_acquired = 0;
data2.num_acquired = 0;
data2.num_lost = 0;
data2.expect_null_connection = FALSE;
data2.num_free_func = 0;
id2 = g_bus_own_name_on_connection (c2,
name,
G_BUS_NAME_OWNER_FLAGS_NONE,
name_acquired_handler,
name_lost_handler,
&data2,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_bus_unown_name (id2);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_assert_cmpint (data2.num_free_func, ==, 1);
/* then with _REPLACE */
data2.num_bus_acquired = 0;
data2.num_acquired = 0;
data2.num_lost = 0;
data2.expect_null_connection = FALSE;
data2.num_free_func = 0;
id2 = g_bus_own_name_on_connection (c2,
name,
G_BUS_NAME_OWNER_FLAGS_REPLACE,
name_acquired_handler,
name_lost_handler,
&data2,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_bus_unown_name (id2);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_assert_cmpint (data2.num_free_func, ==, 1);
/*
* Stop owning the name and grab it again with _ALLOW_REPLACEMENT.
*/
data.expect_null_connection = FALSE;
g_bus_unown_name (id);
g_assert_cmpint (data.num_bus_acquired, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_free_func, ==, 3);
/* grab it again */
data.num_bus_acquired = 0;
data.num_acquired = 0;
data.num_lost = 0;
data.expect_null_connection = FALSE;
id = g_bus_own_name (G_BUS_TYPE_SESSION,
name,
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
bus_acquired_handler,
name_acquired_handler,
name_lost_handler,
&data,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data.num_bus_acquired, ==, 0);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_bus_acquired, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 0);
g_assert_cmpint (data.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_bus_acquired, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_lost, ==, 0);
/*
* Now try to grab the name from the secondary connection.
*
*/
/* first without _REPLACE - this won't make us acquire the name */
data2.num_bus_acquired = 0;
data2.num_acquired = 0;
data2.num_lost = 0;
data2.expect_null_connection = FALSE;
data2.num_free_func = 0;
id2 = g_bus_own_name_on_connection (c2,
name,
G_BUS_NAME_OWNER_FLAGS_NONE,
name_acquired_handler,
name_lost_handler,
&data2,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_bus_unown_name (id2);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_assert_cmpint (data2.num_free_func, ==, 1);
/* then with _REPLACE - here we should acquire the name - e.g. owner should lose it
* and owner2 should acquire it */
data2.num_bus_acquired = 0;
data2.num_acquired = 0;
data2.num_lost = 0;
data2.expect_null_connection = FALSE;
data2.num_free_func = 0;
id2 = g_bus_own_name_on_connection (c2,
name,
G_BUS_NAME_OWNER_FLAGS_REPLACE,
name_acquired_handler,
name_lost_handler,
&data2,
(GDestroyNotify) own_name_data_free_func);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_lost, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 0);
/* wait for handlers for both owner and owner2 to fire */
while (data.num_lost == 0 || data2.num_acquired == 0)
g_main_loop_run (loop);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_lost, ==, 1);
g_assert_cmpint (data2.num_acquired, ==, 1);
g_assert_cmpint (data2.num_lost, ==, 0);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
/* ok, make owner2 release the name - then wait for owner to automagically reacquire it */
g_bus_unown_name (id2);
g_assert_cmpint (data2.num_free_func, ==, 1);
g_main_loop_run (loop);
g_assert_cmpint (data.num_acquired, ==, 2);
g_assert_cmpint (data.num_lost, ==, 1);
/*
* Finally, nuke the bus and check name_lost_handler() is invoked.
*
*/
data.expect_null_connection = TRUE;
session_bus_down ();
while (data.num_lost != 2)
g_main_loop_run (loop);
g_assert_cmpint (data.num_acquired, ==, 2);
g_assert_cmpint (data.num_lost, ==, 2);
g_bus_unown_name (id);
g_assert_cmpint (data.num_free_func, ==, 4);
g_object_unref (c);
g_object_unref (c2);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that g_bus_watch_name() works correctly */
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
gboolean expect_null_connection;
guint num_acquired;
guint num_lost;
guint num_appeared;
guint num_vanished;
guint num_free_func;
} WatchNameData;
static void
watch_name_data_free_func (WatchNameData *data)
{
data->num_free_func++;
g_main_loop_quit (loop);
}
static void
w_bus_acquired_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
}
static void
w_name_acquired_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
WatchNameData *data = user_data;
data->num_acquired += 1;
g_main_loop_quit (loop);
}
static void
w_name_lost_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
WatchNameData *data = user_data;
data->num_lost += 1;
g_main_loop_quit (loop);
}
static void
name_appeared_handler (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
WatchNameData *data = user_data;
if (data->expect_null_connection)
{
g_assert (connection == NULL);
}
else
{
g_assert (connection != NULL);
g_dbus_connection_set_exit_on_close (connection, FALSE);
}
data->num_appeared += 1;
g_main_loop_quit (loop);
}
static void
name_vanished_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
WatchNameData *data = user_data;
if (data->expect_null_connection)
{
g_assert (connection == NULL);
}
else
{
g_assert (connection != NULL);
g_dbus_connection_set_exit_on_close (connection, FALSE);
}
data->num_vanished += 1;
g_main_loop_quit (loop);
}
static void
test_bus_watch_name (void)
{
WatchNameData data;
guint id;
guint owner_id;
/*
* First check that name_vanished_handler() is invoked if there is no bus.
*
* Also make sure name_vanished_handler() isn't invoked when unwatching the name.
*/
data.num_free_func = 0;
data.num_appeared = 0;
data.num_vanished = 0;
data.expect_null_connection = TRUE;
id = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.GDBus.Name1",
G_BUS_NAME_WATCHER_FLAGS_NONE,
name_appeared_handler,
name_vanished_handler,
&data,
(GDestroyNotify) watch_name_data_free_func);
g_assert_cmpint (data.num_appeared, ==, 0);
g_assert_cmpint (data.num_vanished, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_appeared, ==, 0);
g_assert_cmpint (data.num_vanished, ==, 1);
g_bus_unwatch_name (id);
g_assert_cmpint (data.num_appeared, ==, 0);
g_assert_cmpint (data.num_vanished, ==, 1);
g_assert_cmpint (data.num_free_func, ==, 1);
/*
* Now bring up a bus, own a name, and then start watching it.
*/
session_bus_up ();
/* own the name */
data.num_free_func = 0;
data.num_acquired = 0;
data.num_lost = 0;
data.expect_null_connection = FALSE;
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
"org.gtk.GDBus.Name1",
G_BUS_NAME_OWNER_FLAGS_NONE,
w_bus_acquired_handler,
w_name_acquired_handler,
w_name_lost_handler,
&data,
(GDestroyNotify) watch_name_data_free_func);
g_main_loop_run (loop);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_lost, ==, 0);
/* now watch the name */
data.num_appeared = 0;
data.num_vanished = 0;
id = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.GDBus.Name1",
G_BUS_NAME_WATCHER_FLAGS_NONE,
name_appeared_handler,
name_vanished_handler,
&data,
(GDestroyNotify) watch_name_data_free_func);
g_assert_cmpint (data.num_appeared, ==, 0);
g_assert_cmpint (data.num_vanished, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_appeared, ==, 1);
g_assert_cmpint (data.num_vanished, ==, 0);
/*
* Unwatch the name.
*/
g_bus_unwatch_name (id);
g_assert_cmpint (data.num_free_func, ==, 1);
/* unown the name */
g_bus_unown_name (owner_id);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_free_func, ==, 2);
/*
* Create a watcher and then make a name be owned.
*
* This should trigger name_appeared_handler() ...
*/
/* watch the name */
data.num_appeared = 0;
data.num_vanished = 0;
data.num_free_func = 0;
id = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.GDBus.Name1",
G_BUS_NAME_WATCHER_FLAGS_NONE,
name_appeared_handler,
name_vanished_handler,
&data,
(GDestroyNotify) watch_name_data_free_func);
g_assert_cmpint (data.num_appeared, ==, 0);
g_assert_cmpint (data.num_vanished, ==, 0);
g_main_loop_run (loop);
g_assert_cmpint (data.num_appeared, ==, 0);
g_assert_cmpint (data.num_vanished, ==, 1);
/* own the name */
data.num_acquired = 0;
data.num_lost = 0;
data.expect_null_connection = FALSE;
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
"org.gtk.GDBus.Name1",
G_BUS_NAME_OWNER_FLAGS_NONE,
w_bus_acquired_handler,
w_name_acquired_handler,
w_name_lost_handler,
&data,
(GDestroyNotify) watch_name_data_free_func);
while (data.num_acquired == 0 || data.num_appeared == 0)
g_main_loop_run (loop);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_lost, ==, 0);
g_assert_cmpint (data.num_appeared, ==, 1);
g_assert_cmpint (data.num_vanished, ==, 1);
/*
* Nuke the bus and check that the name vanishes and is lost.
*/
data.expect_null_connection = TRUE;
session_bus_down ();
g_main_loop_run (loop);
g_assert_cmpint (data.num_lost, ==, 1);
g_assert_cmpint (data.num_vanished, ==, 2);
g_bus_unwatch_name (id);
g_assert_cmpint (data.num_free_func, ==, 1);
g_bus_unown_name (owner_id);
g_assert_cmpint (data.num_free_func, ==, 2);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
test_validate_names (void)
{
guint n;
static const struct
{
gboolean name;
gboolean unique;
gboolean interface;
const gchar *string;
} names[] = {
{ 1, 0, 1, "valid.well_known.name"},
{ 1, 0, 0, "valid.well-known.name"},
{ 1, 1, 0, ":valid.unique.name"},
{ 0, 0, 0, "invalid.5well_known.name"},
{ 0, 0, 0, "4invalid.5well_known.name"},
{ 1, 1, 0, ":4valid.5unique.name"},
{ 0, 0, 0, ""},
{ 1, 0, 1, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name1"}, /* 255 */
{ 0, 0, 0, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name12"}, /* 256 - too long! */
{ 0, 0, 0, ".starts.with.a.dot"},
{ 0, 0, 0, "contains.invalid;.characters"},
{ 0, 0, 0, "contains.inva/lid.characters"},
{ 0, 0, 0, "contains.inva[lid.characters"},
{ 0, 0, 0, "contains.inva]lid.characters"},
{ 0, 0, 0, "contains.inva_æøå_lid.characters"},
{ 1, 1, 0, ":1.1"},
};
for (n = 0; n < G_N_ELEMENTS (names); n++)
{
if (names[n].name)
g_assert (g_dbus_is_name (names[n].string));
else
g_assert (!g_dbus_is_name (names[n].string));
if (names[n].unique)
g_assert (g_dbus_is_unique_name (names[n].string));
else
g_assert (!g_dbus_is_unique_name (names[n].string));
if (names[n].interface)
g_assert (g_dbus_is_interface_name (names[n].string));
else
g_assert (!g_dbus_is_interface_name (names[n].string));
}
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
gint ret;
g_type_init ();
g_test_init (&argc, &argv, NULL);
loop = g_main_loop_new (NULL, FALSE);
/* all the tests use a session bus with a well-known address that we can bring up and down
* using session_bus_up() and session_bus_down().
*/
g_unsetenv ("DISPLAY");
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
g_test_add_func ("/gdbus/validate-names", test_validate_names);
g_test_add_func ("/gdbus/bus-own-name", test_bus_own_name);
g_test_add_func ("/gdbus/bus-watch-name", test_bus_watch_name);
ret = g_test_run();
g_main_loop_unref (loop);
return ret;
}

746
gio/tests/gdbus-peer.c Normal file
View File

@ -0,0 +1,746 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include <string.h>
/* for open(2) */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <gio/gunixsocketaddress.h>
#include "gdbus-tests.h"
#ifdef G_OS_UNIX
static gboolean is_unix = TRUE;
#else
static gboolean is_unix = FALSE;
#endif
static gchar *test_guid = NULL;
static GMainLoop *service_loop = NULL;
static GDBusServer *server = NULL;
static GMainLoop *loop = NULL;
/* ---------------------------------------------------------------------------------------------------- */
/* Test that peer-to-peer connections work */
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
gboolean accept_connection;
gint num_connection_attempts;
GPtrArray *current_connections;
guint num_method_calls;
gboolean signal_received;
} PeerData;
static const gchar *test_interface_introspection_xml =
"<node>"
" <interface name='org.gtk.GDBus.PeerTestInterface'>"
" <method name='HelloPeer'>"
" <arg type='s' name='greeting' direction='in'/>"
" <arg type='s' name='response' direction='out'/>"
" </method>"
" <method name='EmitSignal'/>"
" <method name='OpenFile'>"
" <arg type='s' name='path' direction='in'/>"
" </method>"
" <signal name='PeerSignal'>"
" <arg type='s' name='a_string'/>"
" </signal>"
" <property type='s' name='PeerProperty' access='read'/>"
" </interface>"
"</node>";
static const GDBusInterfaceInfo *test_interface_introspection_data = NULL;
static void
test_interface_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
PeerData *data = user_data;
data->num_method_calls++;
g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject");
g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface");
if (g_strcmp0 (method_name, "HelloPeer") == 0)
{
const gchar *greeting;
gchar *response;
g_variant_get (parameters, "(s)", &greeting);
response = g_strdup_printf ("You greeted me with '%s'.",
greeting);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(s)", response));
g_free (response);
}
else if (g_strcmp0 (method_name, "EmitSignal") == 0)
{
GError *error;
error = NULL;
g_dbus_connection_emit_signal (connection,
NULL,
"/org/gtk/GDBus/PeerTestObject",
"org.gtk.GDBus.PeerTestInterface",
"PeerSignal",
NULL,
&error);
g_assert_no_error (error);
g_dbus_method_invocation_return_value (invocation, NULL);
}
else if (g_strcmp0 (method_name, "OpenFile") == 0)
{
const gchar *path;
GDBusMessage *reply;
GError *error;
gint fd;
GUnixFDList *fd_list;
g_variant_get (parameters, "(s)", &path);
fd_list = g_unix_fd_list_new ();
error = NULL;
fd = open (path, O_RDONLY);
g_unix_fd_list_append (fd_list, fd, &error);
g_assert_no_error (error);
close (fd);
reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation));
g_dbus_message_set_unix_fd_list (reply, fd_list);
g_object_unref (invocation);
error = NULL;
g_dbus_connection_send_message (connection,
reply,
NULL, /* out_serial */
&error);
g_assert_no_error (error);
g_object_unref (reply);
}
else
{
g_assert_not_reached ();
}
}
static GVariant *
test_interface_get_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject");
g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface");
g_assert_cmpstr (property_name, ==, "PeerProperty");
return g_variant_new_string ("ThePropertyValue");
}
static const GDBusInterfaceVTable test_interface_vtable =
{
test_interface_method_call,
test_interface_get_property,
NULL /* set_property */
};
static void
on_proxy_signal_received (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
PeerData *data = user_data;
data->signal_received = TRUE;
g_assert (sender_name == NULL);
g_assert_cmpstr (signal_name, ==, "PeerSignal");
g_main_loop_quit (loop);
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
on_deny_authenticated_peer (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials,
gpointer user_data)
{
PeerData *data = user_data;
gboolean deny_peer;
data->num_connection_attempts++;
deny_peer = FALSE;
if (!data->accept_connection)
{
deny_peer = TRUE;
g_main_loop_quit (loop);
}
return deny_peer;
}
/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
static void
on_new_connection (GDBusServer *server,
GDBusConnection *connection,
gpointer user_data)
{
PeerData *data = user_data;
GError *error;
guint reg_id;
//g_print ("Client connected.\n"
// "Negotiated capabilities: unix-fd-passing=%d\n",
// g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
g_ptr_array_add (data->current_connections, g_object_ref (connection));
/* export object on the newly established connection */
error = NULL;
reg_id = g_dbus_connection_register_object (connection,
"/org/gtk/GDBus/PeerTestObject",
"org.gtk.GDBus.PeerTestInterface",
test_interface_introspection_data,
&test_interface_vtable,
data,
NULL, /* GDestroyNotify for data */
&error);
g_assert_no_error (error);
g_assert (reg_id > 0);
g_main_loop_quit (loop);
}
static gpointer
service_thread_func (gpointer user_data)
{
PeerData *data = user_data;
GMainContext *service_context;
GDBusAuthObserver *observer;
GError *error;
service_context = g_main_context_new ();
g_main_context_push_thread_default (service_context);
error = NULL;
observer = g_dbus_auth_observer_new ();
server = g_dbus_server_new_sync (is_unix ? "unix:tmpdir=/tmp/gdbus-test-" : "nonce-tcp:",
G_DBUS_SERVER_FLAGS_NONE,
test_guid,
observer,
NULL, /* cancellable */
&error);
g_assert_no_error (error);
g_signal_connect (server,
"new-connection",
G_CALLBACK (on_new_connection),
data);
g_signal_connect (observer,
"deny-authenticated-peer",
G_CALLBACK (on_deny_authenticated_peer),
data);
g_object_unref (observer);
g_dbus_server_start (server);
service_loop = g_main_loop_new (service_context, FALSE);
g_main_loop_run (service_loop);
g_main_context_pop_thread_default (service_context);
g_main_loop_unref (service_loop);
g_main_context_unref (service_context);
/* test code specifically unrefs the server - see below */
g_assert (server == NULL);
return NULL;
}
#if 0
static gboolean
on_incoming_connection (GSocketService *service,
GSocketConnection *socket_connection,
GObject *source_object,
gpointer user_data)
{
PeerData *data = user_data;
if (data->accept_connection)
{
GError *error;
guint reg_id;
GDBusConnection *connection;
error = NULL;
connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection),
test_guid,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
NULL, /* cancellable */
&error);
g_assert_no_error (error);
g_ptr_array_add (data->current_connections, connection);
/* export object on the newly established connection */
error = NULL;
reg_id = g_dbus_connection_register_object (connection,
"/org/gtk/GDBus/PeerTestObject",
"org.gtk.GDBus.PeerTestInterface",
&test_interface_introspection_data,
&test_interface_vtable,
data,
NULL, /* GDestroyNotify for data */
&error);
g_assert_no_error (error);
g_assert (reg_id > 0);
}
else
{
/* don't do anything */
}
data->num_connection_attempts++;
g_main_loop_quit (loop);
/* stops other signal handlers from being invoked */
return TRUE;
}
static gpointer
service_thread_func (gpointer data)
{
GMainContext *service_context;
gchar *socket_path;
GSocketAddress *address;
GError *error;
service_context = g_main_context_new ();
g_main_context_push_thread_default (service_context);
socket_path = g_strdup_printf ("/tmp/gdbus-test-pid-%d", getpid ());
address = g_unix_socket_address_new (socket_path);
service = g_socket_service_new ();
error = NULL;
g_socket_listener_add_address (G_SOCKET_LISTENER (service),
address,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT,
NULL, /* source_object */
NULL, /* effective_address */
&error);
g_assert_no_error (error);
g_signal_connect (service,
"incoming",
G_CALLBACK (on_incoming_connection),
data);
g_socket_service_start (service);
service_loop = g_main_loop_new (service_context, FALSE);
g_main_loop_run (service_loop);
g_main_context_pop_thread_default (service_context);
g_main_loop_unref (service_loop);
g_main_context_unref (service_context);
g_object_unref (address);
g_free (socket_path);
return NULL;
}
#endif
/* ---------------------------------------------------------------------------------------------------- */
#if 0
static gboolean
check_connection (gpointer user_data)
{
PeerData *data = user_data;
guint n;
for (n = 0; n < data->current_connections->len; n++)
{
GDBusConnection *c;
GIOStream *stream;
c = G_DBUS_CONNECTION (data->current_connections->pdata[n]);
stream = g_dbus_connection_get_stream (c);
g_debug ("In check_connection for %d: connection %p, stream %p", n, c, stream);
g_debug ("closed = %d", g_io_stream_is_closed (stream));
GSocket *socket;
socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
g_debug ("socket_closed = %d", g_socket_is_closed (socket));
g_debug ("socket_condition_check = %d", g_socket_condition_check (socket, G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP));
gchar buf[128];
GError *error;
gssize num_read;
error = NULL;
num_read = g_input_stream_read (g_io_stream_get_input_stream (stream),
buf,
128,
NULL,
&error);
if (num_read < 0)
{
g_debug ("error: %s", error->message);
g_error_free (error);
}
else
{
g_debug ("no error, read %d bytes", (gint) num_read);
}
}
return FALSE;
}
static gboolean
on_do_disconnect_in_idle (gpointer data)
{
GDBusConnection *c = G_DBUS_CONNECTION (data);
g_debug ("GDC %p has ref_count %d", c, G_OBJECT (c)->ref_count);
g_dbus_connection_disconnect (c);
g_object_unref (c);
return FALSE;
}
#endif
static void
test_peer (void)
{
GDBusConnection *c;
GDBusConnection *c2;
GDBusProxy *proxy;
GError *error;
PeerData data;
GVariant *value;
GVariant *result;
const gchar *s;
GThread *service_thread;
memset (&data, '\0', sizeof (PeerData));
data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
/* first try to connect when there is no server */
error = NULL;
c = g_dbus_connection_new_for_address_sync (is_unix ? "unix:path=/tmp/gdbus-test-does-not-exist-pid" :
/* NOTE: Even if something is listening on port 12345 the connection
* will fail because the nonce file doesn't exist */
"nonce-tcp:host=localhost,port=12345,noncefile=this-does-not-exist-gdbus",
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
NULL, /* cancellable */
&error);
_g_assert_error_domain (error, G_IO_ERROR);
g_assert (!g_dbus_error_is_remote_error (error));
g_clear_error (&error);
g_assert (c == NULL);
/* bring up a server - we run the server in a different thread to avoid deadlocks */
error = NULL;
service_thread = g_thread_create (service_thread_func,
&data,
TRUE,
&error);
while (service_loop == NULL)
g_thread_yield ();
g_assert (server != NULL);
/* bring up a connection and accept it */
data.accept_connection = TRUE;
error = NULL;
c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
NULL, /* cancellable */
&error);
g_assert_no_error (error);
g_assert (c != NULL);
while (data.current_connections->len < 1)
g_main_loop_run (loop);
g_assert_cmpint (data.current_connections->len, ==, 1);
g_assert_cmpint (data.num_connection_attempts, ==, 1);
//g_assert (g_dbus_connection_get_bus_type (c) == G_BUS_TYPE_NONE);
g_assert (g_dbus_connection_get_unique_name (c) == NULL);
g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid);
/* check that we create a proxy, read properties, receive signals and invoke
* the HelloPeer() method. Since the server runs in another thread it's fine
* to use synchronous blocking API here.
*/
error = NULL;
proxy = g_dbus_proxy_new_sync (c,
G_TYPE_DBUS_PROXY,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
NULL, /* bus_name */
"/org/gtk/GDBus/PeerTestObject",
"org.gtk.GDBus.PeerTestInterface",
NULL, /* GCancellable */
&error);
g_assert_no_error (error);
g_assert (proxy != NULL);
error = NULL;
value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty", &error);
g_assert_no_error (error);
g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue");
/* try invoking a method */
error = NULL;
result = g_dbus_proxy_invoke_method_sync (proxy,
"HelloPeer",
g_variant_new ("(s)", "Hey Peer!"),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL, /* GCancellable */
&error);
g_assert_no_error (error);
g_variant_get (result, "(s)", &s);
g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'.");
g_variant_unref (result);
g_assert_cmpint (data.num_method_calls, ==, 1);
/* make the other peer emit a signal - catch it */
g_signal_connect (proxy,
"g-signal",
G_CALLBACK (on_proxy_signal_received),
&data);
g_assert (!data.signal_received);
g_dbus_proxy_invoke_method (proxy,
"EmitSignal",
NULL, /* no arguments */
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL, /* GCancellable */
NULL, /* GAsyncReadyCallback - we don't care about the result */
NULL); /* user_data */
g_main_loop_run (loop);
g_assert (data.signal_received);
g_assert_cmpint (data.num_method_calls, ==, 2);
/* check for UNIX fd passing */
#ifdef G_OS_UNIX
{
GDBusMessage *method_call_message;
GDBusMessage *method_reply_message;
GUnixFDList *fd_list;
gint fd;
gchar buf[1024];
gssize len;
gchar *buf2;
gsize len2;
method_call_message = g_dbus_message_new_method_call (NULL, /* name */
"/org/gtk/GDBus/PeerTestObject",
"org.gtk.GDBus.PeerTestInterface",
"OpenFile");
g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", "/etc/hosts"));
error = NULL;
method_reply_message = g_dbus_connection_send_message_with_reply_sync (c,
method_call_message,
-1,
NULL, /* out_serial */
NULL, /* cancellable */
&error);
g_assert_no_error (error);
g_assert (g_dbus_message_get_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
fd_list = g_dbus_message_get_unix_fd_list (method_reply_message);
g_assert (fd_list != NULL);
g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1);
error = NULL;
fd = g_unix_fd_list_get (fd_list, 0, &error);
g_assert_no_error (error);
g_object_unref (method_call_message);
g_object_unref (method_reply_message);
memset (buf, '\0', sizeof (buf));
len = read (fd, buf, sizeof (buf) - 1);
close (fd);
error = NULL;
g_file_get_contents ("/etc/hosts",
&buf2,
&len2,
&error);
g_assert_no_error (error);
if (len2 > sizeof (buf))
buf2[sizeof (buf)] = '\0';
g_assert_cmpstr (buf, ==, buf2);
g_free (buf2);
}
#endif /* G_OS_UNIX */
/* bring up a connection - don't accept it - this should fail
*/
data.accept_connection = FALSE;
error = NULL;
c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
NULL, /* cancellable */
&error);
_g_assert_error_domain (error, G_IO_ERROR);
g_assert (c2 == NULL);
#if 0
/* TODO: THIS TEST DOESN'T WORK YET */
/* bring up a connection - accept it.. then disconnect from the client side - check
* that the server side gets the disconnect signal.
*/
error = NULL;
data.accept_connection = TRUE;
c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
NULL, /* cancellable */
&error);
g_assert_no_error (error);
g_assert (c2 != NULL);
g_assert (!g_dbus_connection_get_is_disconnected (c2));
while (data.num_connection_attempts < 3)
g_main_loop_run (loop);
g_assert_cmpint (data.current_connections->len, ==, 2);
g_assert_cmpint (data.num_connection_attempts, ==, 3);
g_assert (!g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1])));
g_idle_add (on_do_disconnect_in_idle, c2);
g_debug ("==================================================");
g_debug ("==================================================");
g_debug ("==================================================");
g_debug ("waiting for disconnect on connection %p, stream %p",
data.current_connections->pdata[1],
g_dbus_connection_get_stream (data.current_connections->pdata[1]));
g_timeout_add (2000, check_connection, &data);
//_g_assert_signal_received (G_DBUS_CONNECTION (data.current_connections->pdata[1]), "closed");
g_main_loop_run (loop);
g_assert (g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1])));
g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */
#endif
/* unref the server and stop listening for new connections
*
* This won't bring down the established connections - check that c is still connected
* by invoking a method
*/
//g_socket_service_stop (service);
//g_object_unref (service);
g_dbus_server_stop (server);
g_object_unref (server);
server = NULL;
error = NULL;
result = g_dbus_proxy_invoke_method_sync (proxy,
"HelloPeer",
g_variant_new ("(s)", "Hey Again Peer!"),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL, /* GCancellable */
&error);
g_assert_no_error (error);
g_variant_get (result, "(s)", &s);
g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'.");
g_variant_unref (result);
g_assert_cmpint (data.num_method_calls, ==, 4);
#if 0
/* TODO: THIS TEST DOESN'T WORK YET */
/* now disconnect from the server side - check that the client side gets the signal */
g_assert_cmpint (data.current_connections->len, ==, 1);
g_assert (G_DBUS_CONNECTION (data.current_connections->pdata[0]) != c);
g_dbus_connection_disconnect (G_DBUS_CONNECTION (data.current_connections->pdata[0]));
if (!g_dbus_connection_get_is_disconnected (c))
_g_assert_signal_received (c, "closed");
g_assert (g_dbus_connection_get_is_disconnected (c));
#endif
g_object_unref (c);
g_ptr_array_unref (data.current_connections);
g_object_unref (proxy);
g_main_loop_quit (service_loop);
g_thread_join (service_thread);
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
gint ret;
GDBusNodeInfo *introspection_data = NULL;
g_type_init ();
g_thread_init (NULL);
g_test_init (&argc, &argv, NULL);
introspection_data = g_dbus_node_info_new_for_xml (test_interface_introspection_xml, NULL);
g_assert (introspection_data != NULL);
test_interface_introspection_data = introspection_data->interfaces[0];
test_guid = g_dbus_generate_guid ();
/* all the tests rely on a shared main loop */
loop = g_main_loop_new (NULL, FALSE);
g_test_add_func ("/gdbus/peer-to-peer", test_peer);
ret = g_test_run();
g_main_loop_unref (loop);
g_free (test_guid);
g_dbus_node_info_unref (introspection_data);
return ret;
}

455
gio/tests/gdbus-proxy.c Normal file
View File

@ -0,0 +1,455 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include <string.h>
#include "gdbus-tests.h"
/* all tests rely on a shared mainloop */
static GMainLoop *loop = NULL;
/* ---------------------------------------------------------------------------------------------------- */
/* Test that the method aspects of GDBusProxy works */
/* ---------------------------------------------------------------------------------------------------- */
static void
test_methods (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy)
{
GVariant *result;
GError *error;
const gchar *str;
gchar *dbus_error_name;
/* check that we can invoke a method */
error = NULL;
result = g_dbus_proxy_invoke_method_sync (proxy,
"HelloWorld",
g_variant_new ("(s)", "Hey"),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "(s)");
g_variant_get (result, "(s)", &str);
g_assert_cmpstr (str, ==, "You greeted me with 'Hey'. Thanks!");
g_variant_unref (result);
/* Check that we can completely recover the returned error */
result = g_dbus_proxy_invoke_method_sync (proxy,
"HelloWorld",
g_variant_new ("(s)", "Yo"),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
g_assert (g_dbus_error_is_remote_error (error));
g_assert (g_dbus_error_is_remote_error (error));
g_assert (result == NULL);
dbus_error_name = g_dbus_error_get_remote_error (error);
g_assert_cmpstr (dbus_error_name, ==, "com.example.TestException");
g_free (dbus_error_name);
g_assert (g_dbus_error_strip_remote_error (error));
g_assert_cmpstr (error->message, ==, "Yo is not a proper greeting");
g_clear_error (&error);
/* Check that we get a timeout if the method handling is taking longer than timeout */
error = NULL;
result = g_dbus_proxy_invoke_method_sync (proxy,
"Sleep",
g_variant_new ("(i)", 500 /* msec */),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
100 /* msec */,
NULL,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
g_assert (!g_dbus_error_is_remote_error (error));
g_assert (result == NULL);
g_clear_error (&error);
/* Check that proxy-default timeouts work. */
g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1);
/* the default timeout is 25000 msec so this should work */
result = g_dbus_proxy_invoke_method_sync (proxy,
"Sleep",
g_variant_new ("(i)", 500 /* msec */),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */
NULL,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result);
/* now set the proxy-default timeout to 250 msec and try the 500 msec call - this should FAIL */
g_dbus_proxy_set_default_timeout (proxy, 250);
g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, 250);
result = g_dbus_proxy_invoke_method_sync (proxy,
"Sleep",
g_variant_new ("(i)", 500 /* msec */),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1, /* use proxy default (e.g. 250 msec) */
NULL,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
g_assert (!g_dbus_error_is_remote_error (error));
g_assert (result == NULL);
g_clear_error (&error);
/* clean up after ourselves */
g_dbus_proxy_set_default_timeout (proxy, -1);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that the property aspects of GDBusProxy works */
/* ---------------------------------------------------------------------------------------------------- */
static void
test_properties (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy)
{
GError *error;
GVariant *variant;
GVariant *variant2;
GVariant *result;
error = NULL;
/*
* Check that we can read cached properties.
*
* No need to test all properties - GVariant has already been tested
*/
variant = g_dbus_proxy_get_cached_property (proxy, "y", &error);
g_assert_no_error (error);
g_assert (variant != NULL);
g_assert_cmpint (g_variant_get_byte (variant), ==, 1);
g_variant_unref (variant);
variant = g_dbus_proxy_get_cached_property (proxy, "o", &error);
g_assert_no_error (error);
g_assert (variant != NULL);
g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path");
g_variant_unref (variant);
/*
* Now ask the service to change a property and check that #GDBusProxy::g-property-changed
* is received. Also check that the cache is updated.
*/
variant2 = g_variant_new_byte (42);
result = g_dbus_proxy_invoke_method_sync (proxy,
"FrobSetProperty",
g_variant_new ("(sv)",
"y",
variant2),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result);
_g_assert_signal_received (proxy, "g-properties-changed");
variant = g_dbus_proxy_get_cached_property (proxy, "y", &error);
g_assert_no_error (error);
g_assert (variant != NULL);
g_assert_cmpint (g_variant_get_byte (variant), ==, 42);
g_variant_unref (variant);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that the signal aspects of GDBusProxy works */
/* ---------------------------------------------------------------------------------------------------- */
static void
test_proxy_signals_on_signal (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
GString *s = user_data;
g_assert_cmpstr (signal_name, ==, "TestSignal");
g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(sov)");
g_variant_print_string (parameters, s, TRUE);
}
typedef struct
{
GMainLoop *internal_loop;
GString *s;
} TestSignalData;
static void
test_proxy_signals_on_emit_signal_cb (GDBusProxy *proxy,
GAsyncResult *res,
gpointer user_data)
{
TestSignalData *data = user_data;
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_proxy_invoke_method_finish (proxy,
res,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result);
/* check that the signal was recieved before we got the method result */
g_assert (strlen (data->s->str) > 0);
/* break out of the loop */
g_main_loop_quit (data->internal_loop);
}
static void
test_signals (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy)
{
GError *error;
GString *s;
gulong signal_handler_id;
TestSignalData data;
GVariant *result;
error = NULL;
/*
* Ask the service to emit a signal and check that we receive it.
*
* Note that blocking calls don't block in the mainloop so wait for the signal (which
* is dispatched before the method reply)
*/
s = g_string_new (NULL);
signal_handler_id = g_signal_connect (proxy,
"g-signal",
G_CALLBACK (test_proxy_signals_on_signal),
s);
result = g_dbus_proxy_invoke_method_sync (proxy,
"EmitSignal",
g_variant_new ("(so)",
"Accept the next proposition you hear",
"/some/path"),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result);
/* check that we haven't received the signal just yet */
g_assert (strlen (s->str) == 0);
/* and now wait for the signal */
_g_assert_signal_received (proxy, "g-signal");
g_assert_cmpstr (s->str,
==,
"('Accept the next proposition you hear .. in bed!', objectpath '/some/path/in/bed', <'a variant'>)");
g_signal_handler_disconnect (proxy, signal_handler_id);
g_string_free (s, TRUE);
/*
* Now do this async to check the signal is received before the method returns.
*/
s = g_string_new (NULL);
data.internal_loop = g_main_loop_new (NULL, FALSE);
data.s = s;
signal_handler_id = g_signal_connect (proxy,
"g-signal",
G_CALLBACK (test_proxy_signals_on_signal),
s);
g_dbus_proxy_invoke_method (proxy,
"EmitSignal",
g_variant_new ("(so)",
"You will make a great programmer",
"/some/other/path"),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb,
&data);
g_main_loop_run (data.internal_loop);
g_main_loop_unref (data.internal_loop);
g_assert_cmpstr (s->str,
==,
"('You will make a great programmer .. in bed!', objectpath '/some/other/path/in/bed', <'a variant'>)");
g_signal_handler_disconnect (proxy, signal_handler_id);
g_string_free (s, TRUE);
}
static void
test_bogus_method_return (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy)
{
GError *error = NULL;
GVariant *result;
result = g_dbus_proxy_invoke_method_sync (proxy,
"PairReturn",
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert (result == NULL);
}
/* ---------------------------------------------------------------------------------------------------- */
static const gchar *frob_dbus_interface_xml =
"<node>"
" <interface name='com.example.Frob'>"
/* Deliberately different from gdbus-testserver.py's definition */
" <method name='PairReturn'>"
" <arg type='u' name='somenumber' direction='in'/>"
" <arg type='s' name='somestring' direction='out'/>"
" </method>"
" <method name='HelloWorld'>"
" <arg type='s' name='somestring' direction='in'/>"
" <arg type='s' name='somestring' direction='out'/>"
" </method>"
" <method name='Sleep'>"
" <arg type='i' name='timeout' direction='in'/>"
" </method>"
" </interface>"
"</node>";
static GDBusInterfaceInfo *frob_dbus_interface_info;
static void
on_proxy_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy,
gpointer user_data)
{
test_methods (connection, name, name_owner, proxy);
test_properties (connection, name, name_owner, proxy);
test_signals (connection, name, name_owner, proxy);
/* Now repeat the method tests, with an expected interface set */
g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info);
test_methods (connection, name, name_owner, proxy);
/* And now one more test where we deliberately set the expected
* interface definition incorrectly
*/
test_bogus_method_return (connection, name, name_owner, proxy);
g_main_loop_quit (loop);
}
static void
on_proxy_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
}
static void
test_proxy (void)
{
guint watcher_id;
session_bus_up ();
/* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
* until one can connect to the bus but that's not how things work right now
*/
usleep (500 * 1000);
watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION,
"com.example.TestService",
G_BUS_NAME_WATCHER_FLAGS_NONE,
"/com/example/TestObject",
"com.example.Frob",
G_TYPE_DBUS_PROXY,
G_DBUS_PROXY_FLAGS_NONE,
on_proxy_appeared,
on_proxy_vanished,
NULL,
NULL);
/* this is safe; testserver will exit once the bus goes away */
g_assert (g_spawn_command_line_async ("./gdbus-testserver.py", NULL));
g_main_loop_run (loop);
g_bus_unwatch_proxy (watcher_id);
/* tear down bus */
session_bus_down ();
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
gint ret;
GDBusNodeInfo *introspection_data = NULL;
g_type_init ();
g_test_init (&argc, &argv, NULL);
introspection_data = g_dbus_node_info_new_for_xml (frob_dbus_interface_xml, NULL);
g_assert (introspection_data != NULL);
frob_dbus_interface_info = introspection_data->interfaces[0];
/* all the tests rely on a shared main loop */
loop = g_main_loop_new (NULL, FALSE);
/* all the tests use a session bus with a well-known address that we can bring up and down
* using session_bus_up() and session_bus_down().
*/
g_unsetenv ("DISPLAY");
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
g_test_add_func ("/gdbus/proxy", test_proxy);
ret = g_test_run();
g_dbus_node_info_unref (introspection_data);
return ret;
}

View File

@ -0,0 +1,650 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <string.h>
#include <unistd.h>
#include <dbus/dbus.h>
/* ---------------------------------------------------------------------------------------------------- */
static void
hexdump (const guchar *str, gsize len)
{
const guchar *data = (const guchar *) str;
guint n, m;
for (n = 0; n < len; n += 16)
{
g_printerr ("%04x: ", n);
for (m = n; m < n + 16; m++)
{
if (m > n && (m%4) == 0)
g_printerr (" ");
if (m < len)
g_printerr ("%02x ", data[m]);
else
g_printerr (" ");
}
g_printerr (" ");
for (m = n; m < len && m < n + 16; m++)
g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.');
g_printerr ("\n");
}
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
append_gv_to_dbus_iter (DBusMessageIter *iter,
GVariant *value,
GError **error)
{
const GVariantType *type;
type = g_variant_get_type (value);
if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
{
dbus_bool_t v = g_variant_get_boolean (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
{
guint8 v = g_variant_get_byte (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
{
gint16 v = g_variant_get_int16 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
{
guint16 v = g_variant_get_uint16 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
{
gint32 v = g_variant_get_int32 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
{
guint32 v = g_variant_get_uint32 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
{
gint64 v = g_variant_get_int64 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
{
guint64 v = g_variant_get_uint64 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
{
gdouble v = g_variant_get_double (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v);
}
else if (g_variant_type_is_variant (type))
{
DBusMessageIter sub;
GVariant *child;
child = g_variant_get_child_value (value, 0);
dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT,
g_variant_get_type_string (child),
&sub);
if (!append_gv_to_dbus_iter (&sub, child, error))
{
g_variant_unref (child);
goto fail;
}
dbus_message_iter_close_container (iter, &sub);
g_variant_unref (child);
}
else if (g_variant_type_is_array (type))
{
DBusMessageIter dbus_iter;
const gchar *type_string;
GVariantIter gv_iter;
GVariant *item;
type_string = g_variant_get_type_string (value);
type_string++; /* skip the 'a' */
dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
type_string, &dbus_iter);
g_variant_iter_init (&gv_iter, value);
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
{
goto fail;
}
}
dbus_message_iter_close_container (iter, &dbus_iter);
}
else if (g_variant_type_is_tuple (type))
{
DBusMessageIter dbus_iter;
GVariantIter gv_iter;
GVariant *item;
dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT,
NULL, &dbus_iter);
g_variant_iter_init (&gv_iter, value);
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
goto fail;
}
dbus_message_iter_close_container (iter, &dbus_iter);
}
else if (g_variant_type_is_dict_entry (type))
{
DBusMessageIter dbus_iter;
GVariant *key, *val;
dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY,
NULL, &dbus_iter);
key = g_variant_get_child_value (value, 0);
if (!append_gv_to_dbus_iter (&dbus_iter, key, error))
{
g_variant_unref (key);
goto fail;
}
g_variant_unref (key);
val = g_variant_get_child_value (value, 1);
if (!append_gv_to_dbus_iter (&dbus_iter, val, error))
{
g_variant_unref (val);
goto fail;
}
g_variant_unref (val);
dbus_message_iter_close_container (iter, &dbus_iter);
}
else
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Error serializing GVariant with type-string `%s' to a D-Bus message",
g_variant_get_type_string (value));
goto fail;
}
return TRUE;
fail:
return FALSE;
}
static gboolean
append_gv_to_dbus_message (DBusMessage *message,
GVariant *value,
GError **error)
{
gboolean ret;
guint n;
ret = FALSE;
if (value != NULL)
{
DBusMessageIter iter;
GVariantIter gv_iter;
GVariant *item;
dbus_message_iter_init_append (message, &iter);
g_variant_iter_init (&gv_iter, value);
n = 0;
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&iter, item, error))
{
g_prefix_error (error,
"Error encoding in-arg %d: ",
n);
goto out;
}
n++;
}
}
ret = TRUE;
out:
return ret;
}
static void
print_gv_dbus_message (GVariant *value)
{
DBusMessage *message;
char *blob;
int blob_len;
GError *error;
message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
dbus_message_set_serial (message, 0x41);
dbus_message_set_path (message, "/foo/bar");
dbus_message_set_member (message, "Member");
error = NULL;
if (!append_gv_to_dbus_message (message, value, &error))
{
g_printerr ("Error printing GVariant as DBusMessage: %s", error->message);
g_error_free (error);
goto out;
}
dbus_message_marshal (message, &blob, &blob_len);
g_printerr ("\n");
hexdump ((guchar *) blob, blob_len);
out:
dbus_message_unref (message);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
dbus_1_message_append (GString *s,
guint indent,
DBusMessageIter *iter)
{
gint arg_type;
DBusMessageIter sub;
g_string_append_printf (s, "%*s", indent, "");
arg_type = dbus_message_iter_get_arg_type (iter);
switch (arg_type)
{
case DBUS_TYPE_BOOLEAN:
{
dbus_bool_t value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "bool: %s\n", value ? "true" : "false");
break;
}
case DBUS_TYPE_BYTE:
{
guchar value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "byte: 0x%02x\n", (guint) value);
break;
}
case DBUS_TYPE_INT16:
{
gint16 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT16:
{
guint16 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value);
break;
}
case DBUS_TYPE_INT32:
{
gint32 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT32:
{
guint32 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value);
break;
}
case DBUS_TYPE_INT64:
{
gint64 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT64:
{
guint64 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value);
break;
}
case DBUS_TYPE_DOUBLE:
{
gdouble value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "double: %f\n", value);
break;
}
case DBUS_TYPE_STRING:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "string: `%s'\n", value);
break;
}
case DBUS_TYPE_OBJECT_PATH:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "object_path: `%s'\n", value);
break;
}
case DBUS_TYPE_SIGNATURE:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "signature: `%s'\n", value);
break;
}
case DBUS_TYPE_VARIANT:
g_string_append_printf (s, "variant:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_ARRAY:
g_string_append_printf (s, "array:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_STRUCT:
g_string_append_printf (s, "struct:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_DICT_ENTRY:
g_string_append_printf (s, "dict_entry:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
default:
g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type `%c' (%d)",
arg_type,
arg_type);
g_assert_not_reached ();
break;
}
}
static gchar *
dbus_1_message_print (DBusMessage *message)
{
GString *s;
guint n;
DBusMessageIter iter;
s = g_string_new (NULL);
n = 0;
dbus_message_iter_init (message, &iter);
while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
{
g_string_append_printf (s, "value %d: ", n);
dbus_1_message_append (s, 2, &iter);
dbus_message_iter_next (&iter);
n++;
}
return g_string_free (s, FALSE);
}
/* ---------------------------------------------------------------------------------------------------- */
static gchar *
get_body_signature (GVariant *value)
{
const gchar *s;
gsize len;
gchar *ret;
s = g_variant_get_type_string (value);
len = strlen (s);
g_assert (len>=2);
ret = g_strndup (s + 1, len - 2);
return ret;
}
static void
check_serialization (GVariant *value,
const gchar *expected_dbus_1_output)
{
guchar *blob;
gsize blob_size;
DBusMessage *dbus_1_message;
GDBusMessage *message;
GDBusMessage *recovered_message;
GError *error;
DBusError dbus_error;
gchar *s;
gchar *s1;
message = g_dbus_message_new ();
g_dbus_message_set_body (message, value);
g_dbus_message_set_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
g_dbus_message_set_serial (message, 0x41);
s = get_body_signature (value);
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s));
g_free (s);
/* First check that the serialization to the D-Bus wire format is correct */
error = NULL;
blob = g_dbus_message_to_blob (message,
&blob_size,
&error);
g_assert_no_error (error);
g_assert (blob != NULL);
dbus_error_init (&dbus_error);
dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error);
if (dbus_error_is_set (&dbus_error))
{
g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n",
dbus_error.name,
dbus_error.message);
hexdump (blob, blob_size);
dbus_error_free (&dbus_error);
s = g_variant_print (value, TRUE);
g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s);
g_free (s);
g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n");
print_gv_dbus_message (value);
g_assert_not_reached ();
}
s = dbus_1_message_print (dbus_1_message);
dbus_message_unref (dbus_1_message);
g_assert_cmpstr (s, ==, expected_dbus_1_output);
g_free (s);
/* Then serialize back and check that the body is identical */
error = NULL;
recovered_message = g_dbus_message_new_from_blob (blob, blob_size, &error);
g_assert_no_error (error);
g_assert (recovered_message != NULL);
g_assert (g_dbus_message_get_body (recovered_message) != NULL);
if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value))
{
s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE);
s1 = g_variant_print (value, TRUE);
g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n",
s,
s1);
g_free (s);
g_free (s1);
g_assert_not_reached ();
}
g_object_unref (message);
g_object_unref (recovered_message);
}
static void
message_serialize_basic (void)
{
check_serialization (g_variant_new ("(sogybnqiuxtd)",
"this is a string",
"/this/is/a/path",
"sad",
42,
TRUE,
-42,
60000,
-44,
100000,
-G_GINT64_CONSTANT(2)<<34,
G_GUINT64_CONSTANT(0xffffffffffffffff),
42.5),
"value 0: string: `this is a string'\n"
"value 1: object_path: `/this/is/a/path'\n"
"value 2: signature: `sad'\n"
"value 3: byte: 0x2a\n"
"value 4: bool: true\n"
"value 5: int16: -42\n"
"value 6: uint16: 60000\n"
"value 7: int32: -44\n"
"value 8: uint32: 100000\n"
"value 9: int64: -34359738368\n"
"value 10: uint64: 18446744073709551615\n"
"value 11: double: 42.500000\n");
}
/* ---------------------------------------------------------------------------------------------------- */
static void
message_serialize_complex (void)
{
GError *error;
GVariant *value;
error = NULL;
value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"),
"([1, 2, 3], {'one': 'white', 'two': 'black'})",
NULL, NULL, &error);
g_assert_no_error (error);
g_assert (value != NULL);
check_serialization (value,
"value 0: array:\n"
" int32: 1\n"
" int32: 2\n"
" int32: 3\n"
"value 1: array:\n"
" dict_entry:\n"
" string: `one'\n"
" string: `white'\n"
" dict_entry:\n"
" string: `two'\n"
" string: `black'\n");
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic);
g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex);
return g_test_run();
}

View File

@ -0,0 +1,342 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include "gdbus-sessionbus.h"
/* ---------------------------------------------------------------------------------------------------- */
/* Utilities for bringing up and tearing down session message bus instances */
static void
watch_parent (gint fd)
{
GPollFD fds[1];
gint num_events;
gchar buf[512];
guint bytes_read;
GArray *buses_to_kill_array;
fds[0].fd = fd;
fds[0].events = G_IO_HUP | G_IO_IN;
fds[0].revents = 0;
buses_to_kill_array = g_array_new (FALSE, TRUE, sizeof (guint));
do
{
guint pid;
guint n;
num_events = g_poll (fds, 1, -1);
if (num_events == 0)
continue;
if (fds[0].revents == G_IO_HUP)
{
for (n = 0; n < buses_to_kill_array->len; n++)
{
pid = g_array_index (buses_to_kill_array, guint, n);
g_print ("cleaning up bus with pid %d\n", pid);
kill (pid, SIGTERM);
}
g_array_free (buses_to_kill_array, TRUE);
exit (0);
}
//g_debug ("data from parent");
memset (buf, '\0', sizeof buf);
again:
bytes_read = read (fds[0].fd, buf, sizeof buf);
if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR))
goto again;
if (sscanf (buf, "add %d\n", &pid) == 1)
{
g_array_append_val (buses_to_kill_array, pid);
}
else if (sscanf (buf, "remove %d\n", &pid) == 1)
{
for (n = 0; n < buses_to_kill_array->len; n++)
{
if (g_array_index (buses_to_kill_array, guint, n) == pid)
{
g_array_remove_index (buses_to_kill_array, n);
pid = 0;
break;
}
}
if (pid != 0)
{
g_warning ("unknown pid %d to remove", pid);
}
}
else
{
g_warning ("unknown command from parent '%s'", buf);
}
}
while (TRUE);
}
static GHashTable *session_bus_address_to_pid = NULL;
static gint pipe_fds[2];
const gchar *
session_bus_up_with_address (const gchar *given_address)
{
gchar *address;
int stdout_fd;
GError *error;
gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL};
GPid pid;
gchar buf[512];
ssize_t bytes_read;
gchar *config_file_name;
gint config_file_fd;
GString *config_file_contents;
address = NULL;
error = NULL;
config_file_name = NULL;
config_file_fd = -1;
argv[2] = NULL;
config_file_fd = g_file_open_tmp ("g-dbus-tests-XXXXXX",
&config_file_name,
&error);
if (config_file_fd < 0)
{
g_warning ("Error creating temporary config file: %s", error->message);
g_error_free (error);
goto out;
}
config_file_contents = g_string_new (NULL);
g_string_append (config_file_contents, "<busconfig>\n");
g_string_append (config_file_contents, " <type>session</type>\n");
g_string_append_printf (config_file_contents, " <listen>%s</listen>\n", given_address);
g_string_append (config_file_contents,
" <policy context=\"default\">\n"
" <!-- Allow everything to be sent -->\n"
" <allow send_destination=\"*\" eavesdrop=\"true\"/>\n"
" <!-- Allow everything to be received -->\n"
" <allow eavesdrop=\"true\"/>\n"
" <!-- Allow anyone to own anything -->\n"
" <allow own=\"*\"/>\n"
" </policy>\n");
g_string_append (config_file_contents, "</busconfig>\n");
if (write (config_file_fd, config_file_contents->str, config_file_contents->len) != (gssize) config_file_contents->len)
{
g_warning ("Error writing %d bytes to config file: %m", (gint) config_file_contents->len);
g_string_free (config_file_contents, TRUE);
goto out;
}
g_string_free (config_file_contents, TRUE);
argv[2] = g_strdup_printf ("--config-file=%s", config_file_name);
if (session_bus_address_to_pid == NULL)
{
/* keep a mapping from session bus address to the pid */
session_bus_address_to_pid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
/* fork a child to clean up session buses when we are killed */
if (pipe (pipe_fds) != 0)
{
g_warning ("pipe() failed: %m");
g_assert_not_reached ();
}
switch (fork ())
{
case -1:
g_warning ("fork() failed: %m");
g_assert_not_reached ();
break;
case 0:
/* child */
close (pipe_fds[1]);
watch_parent (pipe_fds[0]);
break;
default:
/* parent */
close (pipe_fds[0]);
break;
}
//atexit (cleanup_session_buses);
/* TODO: need to handle the cases where we crash */
}
else
{
/* check if we already have a bus running for this address */
if (g_hash_table_lookup (session_bus_address_to_pid, given_address) != NULL)
{
g_warning ("Already have a bus instance for the given address %s", given_address);
goto out;
}
}
if (!g_spawn_async_with_pipes (NULL,
argv,
NULL,
G_SPAWN_SEARCH_PATH,
NULL,
NULL,
&pid,
NULL,
&stdout_fd,
NULL,
&error))
{
g_warning ("Error spawning dbus-daemon: %s", error->message);
g_error_free (error);
goto out;
}
memset (buf, '\0', sizeof buf);
again:
bytes_read = read (stdout_fd, buf, sizeof buf);
if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR))
goto again;
close (stdout_fd);
if (bytes_read == 0 || bytes_read == sizeof buf)
{
g_warning ("Error reading address from dbus daemon, %d bytes read", (gint) bytes_read);
kill (SIGTERM, pid);
goto out;
}
address = g_strdup (buf);
g_strstrip (address);
/* write the pid to the child so it can kill it when we die */
g_snprintf (buf, sizeof buf, "add %d\n", (guint) pid);
write (pipe_fds[1], buf, strlen (buf));
/* start dbus-monitor */
if (g_getenv ("G_DBUS_MONITOR") != NULL)
{
g_spawn_command_line_async ("dbus-monitor --session", NULL);
usleep (500 * 1000);
}
g_hash_table_insert (session_bus_address_to_pid, address, GUINT_TO_POINTER (pid));
out:
if (config_file_fd > 0)
{
if (close (config_file_fd) != 0)
{
g_warning ("Error closing fd for config file %s: %m", config_file_name);
}
g_assert (config_file_name != NULL);
if (unlink (config_file_name) != 0)
{
g_warning ("Error unlinking config file %s: %m", config_file_name);
}
}
g_free (argv[2]);
g_free (config_file_name);
return address;
}
void
session_bus_down_with_address (const gchar *address)
{
gpointer value;
GPid pid;
gchar buf[512];
g_assert (address != NULL);
g_assert (session_bus_address_to_pid != NULL);
value = g_hash_table_lookup (session_bus_address_to_pid, address);
g_assert (value != NULL);
pid = GPOINTER_TO_UINT (g_hash_table_lookup (session_bus_address_to_pid, address));
kill (pid, SIGTERM);
/* write the pid to the child so it won't kill it when we die */
g_snprintf (buf, sizeof buf, "remove %d\n", (guint) pid);
write (pipe_fds[1], buf, strlen (buf));
g_hash_table_remove (session_bus_address_to_pid, address);
}
static gchar *temporary_address = NULL;
static gchar *temporary_address_used_by_bus = NULL;
const gchar *
session_bus_get_temporary_address (void)
{
if (temporary_address == NULL)
{
/* TODO: maybe use a more random name etc etc */
temporary_address = g_strdup_printf ("unix:path=/tmp/g-dbus-tests-pid-%d", getpid ());
}
return temporary_address;
}
const gchar *
session_bus_up (void)
{
if (temporary_address_used_by_bus != NULL)
{
g_warning ("There is already a session bus up");
goto out;
}
temporary_address_used_by_bus = g_strdup (session_bus_up_with_address (session_bus_get_temporary_address ()));
out:
return temporary_address_used_by_bus;
}
void
session_bus_down (void)
{
if (temporary_address_used_by_bus == NULL)
{
g_warning ("There is not a session bus up");
}
else
{
session_bus_down_with_address (temporary_address_used_by_bus);
g_free (temporary_address_used_by_bus);
temporary_address_used_by_bus = NULL;
}
}

View File

@ -0,0 +1,38 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __SESSION_BUS_H__
#define __SESSION_BUS_H__
#include <gio/gio.h>
G_BEGIN_DECLS
const gchar *session_bus_up_with_address (const gchar *given_address);
void session_bus_down_with_address (const gchar *address);
const gchar *session_bus_get_temporary_address (void);
const gchar *session_bus_up (void);
void session_bus_down (void);
G_END_DECLS
#endif /* __SESSION_BUS_H__ */

218
gio/tests/gdbus-tests.c Normal file
View File

@ -0,0 +1,218 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include "gdbus-tests.h"
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
GMainLoop *loop;
gboolean timed_out;
} PropertyNotifyData;
static void
on_property_notify (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
PropertyNotifyData *data = user_data;
g_main_loop_quit (data->loop);
}
static gboolean
on_property_notify_timeout (gpointer user_data)
{
PropertyNotifyData *data = user_data;
data->timed_out = TRUE;
g_main_loop_quit (data->loop);
return TRUE;
}
gboolean
_g_assert_property_notify_run (gpointer object,
const gchar *property_name)
{
gchar *s;
gulong handler_id;
guint timeout_id;
PropertyNotifyData data;
data.loop = g_main_loop_new (NULL, FALSE);
data.timed_out = FALSE;
s = g_strdup_printf ("notify::%s", property_name);
handler_id = g_signal_connect (object,
s,
G_CALLBACK (on_property_notify),
&data);
g_free (s);
timeout_id = g_timeout_add (5 * 1000,
on_property_notify_timeout,
&data);
g_main_loop_run (data.loop);
g_signal_handler_disconnect (object, handler_id);
g_source_remove (timeout_id);
g_main_loop_unref (data.loop);
return data.timed_out;
}
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
GMainLoop *loop;
gboolean timed_out;
} SignalReceivedData;
static void
on_signal_received (gpointer user_data)
{
SignalReceivedData *data = user_data;
g_main_loop_quit (data->loop);
}
static gboolean
on_signal_received_timeout (gpointer user_data)
{
SignalReceivedData *data = user_data;
data->timed_out = TRUE;
g_main_loop_quit (data->loop);
return TRUE;
}
gboolean
_g_assert_signal_received_run (gpointer object,
const gchar *signal_name)
{
gulong handler_id;
guint timeout_id;
SignalReceivedData data;
data.loop = g_main_loop_new (NULL, FALSE);
data.timed_out = FALSE;
handler_id = g_signal_connect_swapped (object,
signal_name,
G_CALLBACK (on_signal_received),
&data);
timeout_id = g_timeout_add (5 * 1000,
on_signal_received_timeout,
&data);
g_main_loop_run (data.loop);
g_signal_handler_disconnect (object, handler_id);
g_source_remove (timeout_id);
g_main_loop_unref (data.loop);
return data.timed_out;
}
/* ---------------------------------------------------------------------------------------------------- */
GDBusConnection *
_g_bus_get_priv (GBusType bus_type,
GCancellable *cancellable,
GError **error)
{
gchar *address;
GDBusConnection *ret;
ret = NULL;
address = g_dbus_address_get_for_bus_sync (bus_type, cancellable, error);
if (address == NULL)
goto out;
ret = g_dbus_connection_new_for_address_sync (address,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
cancellable,
error);
g_free (address);
out:
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
{
GMainLoop *loop;
gboolean timed_out;
} WaitSingleRefData;
static gboolean
on_wait_single_ref_timeout (gpointer user_data)
{
WaitSingleRefData *data = user_data;
data->timed_out = TRUE;
g_main_loop_quit (data->loop);
return TRUE;
}
static void
on_wait_for_single_ref_toggled (gpointer user_data,
GObject *object,
gboolean is_last_ref)
{
WaitSingleRefData *data = user_data;
g_main_loop_quit (data->loop);
}
gboolean
_g_object_wait_for_single_ref_do (gpointer object)
{
WaitSingleRefData data;
guint timeout_id;
data.timed_out = FALSE;
if (G_OBJECT (object)->ref_count == 1)
goto out;
data.loop = g_main_loop_new (NULL, FALSE);
timeout_id = g_timeout_add (5 * 1000,
on_wait_single_ref_timeout,
&data);
g_object_add_toggle_ref (G_OBJECT (object),
on_wait_for_single_ref_toggled,
&data);
g_object_unref (object);
g_main_loop_run (data.loop);
g_object_ref (object);
g_object_remove_toggle_ref (object,
on_wait_for_single_ref_toggled,
&data);
g_source_remove (timeout_id);
g_main_loop_unref (data.loop);
out:
return data.timed_out;
}
/* ---------------------------------------------------------------------------------------------------- */

146
gio/tests/gdbus-tests.h Normal file
View File

@ -0,0 +1,146 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __TESTS_H__
#define __TESTS_H__
#include <gio/gio.h>
#include "gdbus-sessionbus.h"
G_BEGIN_DECLS
/* TODO: clean up and move to gtestutils.c
*
* This is needed because libdbus-1 does not give predictable error messages - e.g. you
* get a different error message on connecting to a bus if the socket file is there vs
* if the socket file is missing.
*/
#define _g_assert_error_domain(err, dom) do { if (!err || (err)->domain != dom) \
g_assertion_message_error (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
#err, err, dom, -1); } while (0)
#define _g_assert_property_notify(object, property_name) \
do \
{ \
if (!G_IS_OBJECT (object)) \
{ \
g_assertion_message (G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"Not a GObject instance"); \
} \
if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), \
property_name) == NULL) \
{ \
g_assertion_message (G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"Property " property_name " does not " \
"exist on object"); \
} \
if (_g_assert_property_notify_run (object, property_name)) \
{ \
g_assertion_message (G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"Timed out waiting for notification " \
"on property " property_name); \
} \
} \
while (FALSE)
#define _g_assert_signal_received(object, signal_name) \
do \
{ \
if (!G_IS_OBJECT (object)) \
{ \
g_assertion_message (G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"Not a GObject instance"); \
} \
if (g_signal_lookup (signal_name, \
G_TYPE_FROM_INSTANCE (object)) == 0) \
{ \
g_assertion_message (G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"Signal `" signal_name "' does not " \
"exist on object"); \
} \
if (_g_assert_signal_received_run (object, signal_name)) \
{ \
g_assertion_message (G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"Timed out waiting for signal `" \
signal_name "'"); \
} \
} \
while (FALSE)
gboolean _g_assert_property_notify_run (gpointer object,
const gchar *property_name);
gboolean _g_assert_signal_received_run (gpointer object,
const gchar *signal_name);
GDBusConnection *_g_bus_get_priv (GBusType bus_type,
GCancellable *cancellable,
GError **error);
#define _g_object_wait_for_single_ref(object) \
do \
{ \
if (!G_IS_OBJECT (object)) \
{ \
g_assertion_message (G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"Not a GObject instance"); \
} \
if (_g_object_wait_for_single_ref_do (object)) \
{ \
g_assertion_message (G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"Timed out waiting for single ref"); \
} \
} \
while (FALSE)
gboolean _g_object_wait_for_single_ref_do (gpointer object);
G_END_DECLS
#endif /* __TESTS_H__ */

270
gio/tests/gdbus-testserver.py Executable file
View File

@ -0,0 +1,270 @@
#!/usr/bin/env python
import gobject
import time
import dbus
import dbus.service
import dbus.mainloop.glib
class TestException(dbus.DBusException):
_dbus_error_name = 'com.example.TestException'
class TestService(dbus.service.Object):
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='s', out_signature='s')
def HelloWorld(self, hello_message):
if str(hello_message) == 'Yo':
raise TestException('Yo is not a proper greeting')
else:
return "You greeted me with '%s'. Thanks!"%(str(hello_message))
@dbus.service.method("com.example.Frob",
in_signature='ss', out_signature='ss')
def DoubleHelloWorld(self, hello1, hello2):
return ("You greeted me with '%s'. Thanks!"%(str(hello1)), "Yo dawg, you uttered '%s'. Thanks!"%(str(hello2)))
@dbus.service.method("com.example.Frob",
in_signature='', out_signature='su')
def PairReturn(self):
return ("foo", 42)
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='ybnqiuxtdsog', out_signature='ybnqiuxtdsog')
def TestPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double, val_string, val_objpath, val_signature):
return val_byte + 1, not val_boolean, val_int16 + 1, val_uint16 + 1, val_int32 + 1, val_uint32 + 1, val_int64 + 1, val_uint64 + 1, -val_double + 0.123, val_string * 2, val_objpath + "/modified", val_signature * 2
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='ayabanaqaiauaxatad', out_signature='ayabanaqaiauaxatad')
def TestArrayOfPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double):
return val_byte*2, val_boolean*2, val_int16*2, val_uint16*2, val_int32*2, val_uint32*2, val_int64*2, val_uint64*2, val_double*2
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='asaoag', out_signature='asaoag')
def TestArrayOfStringTypes(self, val_string, val_objpath, val_signature):
return val_string * 2, val_objpath * 2, val_signature * 2
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature = 'a{yy}a{bb}a{nn}a{qq}a{ii}a{uu}a{xx}a{tt}a{dd}a{ss}a{oo}a{gg}',
out_signature = 'a{yy}a{bb}a{nn}a{qq}a{ii}a{uu}a{xx}a{tt}a{dd}a{ss}a{oo}a{gg}')
def TestHashTables(self, hyy, hbb, hnn, hqq, hii, huu, hxx, htt, hdd, hss, hoo, hgg):
ret_hyy = {}
for i in hyy:
ret_hyy[i*2] = (hyy[i]*3) & 255
ret_hbb = {}
for i in hbb:
ret_hbb[i] = True
ret_hnn = {}
for i in hnn:
ret_hnn[i*2] = hnn[i]*3
ret_hqq = {}
for i in hqq:
ret_hqq[i*2] = hqq[i]*3
ret_hii = {}
for i in hii:
ret_hii[i*2] = hii[i]*3
ret_huu = {}
for i in huu:
ret_huu[i*2] = huu[i]*3
ret_hxx = {}
for i in hxx:
ret_hxx[i + 2] = hxx[i] + 1
ret_htt = {}
for i in htt:
ret_htt[i + 2] = htt[i] + 1
ret_hdd = {}
for i in hdd:
ret_hdd[i + 2.5] = hdd[i] + 5.0
ret_hss = {}
for i in hss:
ret_hss[i + "mod"] = hss[i]*2
ret_hoo = {}
for i in hoo:
ret_hoo[i + "/mod"] = hoo[i] + "/mod2"
ret_hgg = {}
for i in hgg:
ret_hgg[i + "assgit"] = hgg[i]*2
return ret_hyy, ret_hbb, ret_hnn, ret_hqq, ret_hii, ret_huu, ret_hxx, ret_htt, ret_hdd, ret_hss, ret_hoo, ret_hgg
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='(ii)(s(ii)aya{ss})', out_signature='(ii)(s(ii)aya{ss})')
def TestStructureTypes(self, s1, s2):
(x, y) = s1;
(desc, (x1, y1), ay, hss) = s2;
ret_hss = {}
for i in hss:
ret_hss[i] = hss[i] + " ... in bed!"
return (x + 1, y + 1), (desc + " ... in bed!", (x1 + 2, y1 + 2), ay * 2, ret_hss)
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='vb', out_signature='v')
def TestVariant(self, v, modify):
if modify:
if type(v)==dbus.Boolean:
ret = False
elif type(v)==dbus.Dictionary:
ret = {}
for i in v:
ret[i] = v[i] * 2
elif type(v)==dbus.Struct:
ret = ["other struct", dbus.Int16(100)]
else:
ret = v * 2
else:
ret = v
return (type(v))(ret)
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='a(ii)aa(ii)aasaa{ss}aayavaav', out_signature='a(ii)aa(ii)aasaa{ss}aayavaav')
def TestComplexArrays(self, aii, aaii, aas, ahashes, aay, av, aav):
return aii * 2, aaii * 2, aas * 2, ahashes * 2, aay * 2, av *2, aav * 2
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='a{s(ii)}a{sv}a{sav}a{saav}a{sa(ii)}a{sa{ss}}',
out_signature='a{s(ii)}a{sv}a{sav}a{saav}a{sa(ii)}a{sa{ss}}')
def TestComplexHashTables(self, h_str_to_pair, h_str_to_variant, h_str_to_av, h_str_to_aav,
h_str_to_array_of_pairs, hash_of_hashes):
ret_h_str_to_pair = {}
for i in h_str_to_pair:
ret_h_str_to_pair[i + "_baz"] = h_str_to_pair[i]
ret_h_str_to_variant = {}
for i in h_str_to_variant:
ret_h_str_to_variant[i + "_baz"] = h_str_to_variant[i]
return ret_h_str_to_pair, ret_h_str_to_variant, h_str_to_av, h_str_to_aav, h_str_to_array_of_pairs, hash_of_hashes
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='', out_signature='')
def Quit(self):
mainloop.quit()
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob",
in_signature='sv', out_signature='')
def FrobSetProperty(self, prop_name, prop_value):
self.frob_props[prop_name] = prop_value
message = dbus.lowlevel.SignalMessage("/com/example/TestObject",
"org.freedesktop.DBus.Properties",
"PropertiesChanged")
message.append("com.example.Frob")
message.append({prop_name : prop_value})
session_bus.send_message(message)
# ----------------------------------------------------------------------------------------------------
@dbus.service.signal("com.example.Frob",
signature="sov")
def TestSignal(self, str1, objpath1, variant1):
pass
@dbus.service.method("com.example.Frob",
in_signature='so', out_signature='')
def EmitSignal(self, str1, objpath1):
self.TestSignal (str1 + " .. in bed!", objpath1 + "/in/bed", "a variant")
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("com.example.Frob", in_signature='i', out_signature='',
async_callbacks=('return_cb', 'raise_cb'))
def Sleep(self, msec, return_cb, raise_cb):
def return_from_async_wait():
return_cb()
return False
gobject.timeout_add(msec, return_from_async_wait)
# ----------------------------------------------------------------------------------------------------
@dbus.service.method("org.freedesktop.DBus.Properties",
in_signature = 'ss',
out_signature = 'v')
def Get(self, interface_name, property_name):
if interface_name == "com.example.Frob":
return self.frob_props[property_name]
else:
raise TestException("No such interface " + interface_name)
@dbus.service.method("org.freedesktop.DBus.Properties",
in_signature = 's',
out_signature = 'a{sv}')
def GetAll(self, interface_name):
if interface_name == "com.example.Frob":
return self.frob_props
else:
raise TestException("No such interface " + interface_name)
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
session_bus = dbus.SessionBus()
name = dbus.service.BusName("com.example.TestService", session_bus)
obj = TestService(session_bus, '/com/example/TestObject')
#print "Our unique name is %s"%(session_bus.get_unique_name())
obj.frob_props = {}
obj.frob_props["y"] = dbus.Byte(1)
obj.frob_props["b"] = dbus.Boolean(True)
obj.frob_props["n"] = dbus.Int16(2)
obj.frob_props["q"] = dbus.UInt16(3)
obj.frob_props["i"] = dbus.Int32(4)
obj.frob_props["u"] = dbus.UInt32(5)
obj.frob_props["x"] = dbus.Int64(6)
obj.frob_props["t"] = dbus.UInt64(7)
obj.frob_props["d"] = dbus.Double(7.5)
obj.frob_props["s"] = dbus.String("a string")
obj.frob_props["o"] = dbus.ObjectPath("/some/path")
obj.frob_props["ay"] = [dbus.Byte(1), dbus.Byte(11)]
obj.frob_props["ab"] = [dbus.Boolean(True), dbus.Boolean(False)]
obj.frob_props["an"] = [dbus.Int16(2), dbus.Int16(12)]
obj.frob_props["aq"] = [dbus.UInt16(3), dbus.UInt16(13)]
obj.frob_props["ai"] = [dbus.Int32(4), dbus.Int32(14)]
obj.frob_props["au"] = [dbus.UInt32(5), dbus.UInt32(15)]
obj.frob_props["ax"] = [dbus.Int64(6), dbus.Int64(16)]
obj.frob_props["at"] = [dbus.UInt64(7), dbus.UInt64(17)]
obj.frob_props["ad"] = [dbus.Double(7.5), dbus.Double(17.5)]
obj.frob_props["as"] = [dbus.String("a string"), dbus.String("another string")]
obj.frob_props["ao"] = [dbus.ObjectPath("/some/path"), dbus.ObjectPath("/another/path")]
obj.frob_props["foo"] = "a frobbed string"
mainloop = gobject.MainLoop()
mainloop.run()

532
gio/tests/gdbus-threading.c Normal file
View File

@ -0,0 +1,532 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <gio/gio.h>
#include <unistd.h>
#include <string.h>
#include "gdbus-tests.h"
/* all tests rely on a global connection */
static GDBusConnection *c = NULL;
/* all tests rely on a shared mainloop */
static GMainLoop *loop = NULL;
/* ---------------------------------------------------------------------------------------------------- */
/* Ensure that signal and method replies are delivered in the right thread */
/* ---------------------------------------------------------------------------------------------------- */
typedef struct {
GThread *thread;
GMainLoop *thread_loop;
guint signal_count;
} DeliveryData;
static void
msg_cb_expect_success (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
DeliveryData *data = user_data;
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_invoke_method_finish (connection,
res,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_variant_unref (result);
g_assert (g_thread_self () == data->thread);
g_main_loop_quit (data->thread_loop);
}
static void
msg_cb_expect_error_cancelled (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
DeliveryData *data = user_data;
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_invoke_method_finish (connection,
res,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_assert (!g_dbus_error_is_remote_error (error));
g_error_free (error);
g_assert (result == NULL);
g_assert (g_thread_self () == data->thread);
g_main_loop_quit (data->thread_loop);
}
static void
signal_handler (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
DeliveryData *data = user_data;
g_assert (g_thread_self () == data->thread);
data->signal_count++;
g_main_loop_quit (data->thread_loop);
}
static gpointer
test_delivery_in_thread_func (gpointer _data)
{
GMainLoop *thread_loop;
GMainContext *thread_context;
DeliveryData data;
GCancellable *ca;
guint subscription_id;
GDBusConnection *priv_c;
GError *error;
error = NULL;
thread_context = g_main_context_new ();
thread_loop = g_main_loop_new (thread_context, FALSE);
g_main_context_push_thread_default (thread_context);
data.thread = g_thread_self ();
data.thread_loop = thread_loop;
data.signal_count = 0;
/* ---------------------------------------------------------------------------------------------------- */
/*
* Check that we get a reply to the GetId() method call.
*/
g_dbus_connection_invoke_method (c,
"org.freedesktop.DBus", /* bus_name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetId", /* method name */
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) msg_cb_expect_success,
&data);
g_main_loop_run (thread_loop);
/*
* Check that we never actually send a message if the GCancellable
* is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED
* when the actual connection is not up.
*/
ca = g_cancellable_new ();
g_cancellable_cancel (ca);
g_dbus_connection_invoke_method (c,
"org.freedesktop.DBus", /* bus_name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetId", /* method name */
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
ca,
(GAsyncReadyCallback) msg_cb_expect_error_cancelled,
&data);
g_main_loop_run (thread_loop);
g_object_unref (ca);
/*
* Check that cancellation works when the message is already in flight.
*/
ca = g_cancellable_new ();
g_dbus_connection_invoke_method (c,
"org.freedesktop.DBus", /* bus_name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetId", /* method name */
NULL,
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
ca,
(GAsyncReadyCallback) msg_cb_expect_error_cancelled,
&data);
g_cancellable_cancel (ca);
g_main_loop_run (thread_loop);
g_object_unref (ca);
/*
* Check that signals are delivered to the correct thread.
*
* First we subscribe to the signal, then we create a a private
* connection. This should cause a NameOwnerChanged message from
* the message bus.
*/
subscription_id = g_dbus_connection_signal_subscribe (c,
"org.freedesktop.DBus", /* sender */
"org.freedesktop.DBus", /* interface */
"NameOwnerChanged", /* member */
"/org/freedesktop/DBus", /* path */
NULL,
signal_handler,
&data,
NULL);
g_assert (subscription_id != 0);
g_assert (data.signal_count == 0);
priv_c = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (priv_c != NULL);
g_main_loop_run (thread_loop);
g_assert (data.signal_count == 1);
g_object_unref (priv_c);
g_dbus_connection_signal_unsubscribe (c, subscription_id);
/* ---------------------------------------------------------------------------------------------------- */
g_main_context_pop_thread_default (thread_context);
g_main_loop_unref (thread_loop);
g_main_context_unref (thread_context);
g_main_loop_quit (loop);
return NULL;
}
static void
test_delivery_in_thread (void)
{
GError *error;
GThread *thread;
error = NULL;
thread = g_thread_create (test_delivery_in_thread_func,
NULL,
TRUE,
&error);
g_assert_no_error (error);
g_assert (thread != NULL);
/* run the event loop - it is needed to dispatch D-Bus messages */
g_main_loop_run (loop);
g_thread_join (thread);
}
/* ---------------------------------------------------------------------------------------------------- */
typedef struct {
GDBusProxy *proxy;
gint msec;
guint num;
gboolean async;
GMainLoop *thread_loop;
GThread *thread;
gboolean done;
} SyncThreadData;
static void
sleep_cb (GDBusProxy *proxy,
GAsyncResult *res,
gpointer user_data)
{
SyncThreadData *data = user_data;
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_proxy_invoke_method_finish (proxy,
res,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result);
g_assert (data->thread == g_thread_self ());
g_main_loop_quit (data->thread_loop);
//g_debug ("async cb (%p)", g_thread_self ());
}
static gpointer
test_sleep_in_thread_func (gpointer _data)
{
SyncThreadData *data = _data;
GMainContext *thread_context;
guint n;
thread_context = g_main_context_new ();
data->thread_loop = g_main_loop_new (thread_context, FALSE);
g_main_context_push_thread_default (thread_context);
data->thread = g_thread_self ();
for (n = 0; n < data->num; n++)
{
if (data->async)
{
//g_debug ("invoking async (%p)", g_thread_self ());
g_dbus_proxy_invoke_method (data->proxy,
"Sleep",
g_variant_new ("(i)", data->msec),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) sleep_cb,
data);
g_main_loop_run (data->thread_loop);
g_print ("A");
//g_debug ("done invoking async (%p)", g_thread_self ());
}
else
{
GError *error;
GVariant *result;
error = NULL;
//g_debug ("invoking sync (%p)", g_thread_self ());
result = g_dbus_proxy_invoke_method_sync (data->proxy,
"Sleep",
g_variant_new ("(i)", data->msec),
G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-1,
NULL,
&error);
g_print ("S");
//g_debug ("done invoking sync (%p)", g_thread_self ());
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result);
}
}
g_main_context_pop_thread_default (thread_context);
g_main_loop_unref (data->thread_loop);
g_main_context_unref (thread_context);
data->done = TRUE;
g_main_loop_quit (loop);
return NULL;
}
static void
on_proxy_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
GDBusProxy *proxy,
gpointer user_data)
{
guint n;
/*
* Check that multiple threads can do calls without interferring with
* each other. We do this by creating three threads that call the
* Sleep() method on the server (which handles it asynchronously, e.g.
* it won't block other requests) with different sleep durations and
* a number of times. We do this so each set of calls add up to 4000
* milliseconds.
*
* We run this test twice - first with async calls in each thread, then
* again with sync calls
*/
for (n = 0; n < 2; n++)
{
gboolean do_async;
GThread *thread1;
GThread *thread2;
GThread *thread3;
SyncThreadData data1;
SyncThreadData data2;
SyncThreadData data3;
GError *error;
GTimeVal start_time;
GTimeVal end_time;
guint elapsed_msec;
error = NULL;
do_async = (n == 0);
g_get_current_time (&start_time);
data1.proxy = proxy;
data1.msec = 40;
data1.num = 100;
data1.async = do_async;
data1.done = FALSE;
thread1 = g_thread_create (test_sleep_in_thread_func,
&data1,
TRUE,
&error);
g_assert_no_error (error);
g_assert (thread1 != NULL);
data2.proxy = proxy;
data2.msec = 20;
data2.num = 200;
data2.async = do_async;
data2.done = FALSE;
thread2 = g_thread_create (test_sleep_in_thread_func,
&data2,
TRUE,
&error);
g_assert_no_error (error);
g_assert (thread2 != NULL);
data3.proxy = proxy;
data3.msec = 100;
data3.num = 40;
data3.async = do_async;
data3.done = FALSE;
thread3 = g_thread_create (test_sleep_in_thread_func,
&data3,
TRUE,
&error);
g_assert_no_error (error);
g_assert (thread3 != NULL);
/* we handle messages in the main loop - threads will quit it when they are done */
while (!(data1.done && data2.done && data3.done))
g_main_loop_run (loop);
g_thread_join (thread1);
g_thread_join (thread2);
g_thread_join (thread3);
g_get_current_time (&end_time);
elapsed_msec = ((end_time.tv_sec * G_USEC_PER_SEC + end_time.tv_usec) -
(start_time.tv_sec * G_USEC_PER_SEC + start_time.tv_usec)) / 1000;
//g_debug ("Elapsed time for %s = %d msec", n == 0 ? "async" : "sync", elapsed_msec);
/* elapsed_msec should be 4000 msec + change for overhead */
g_assert_cmpint (elapsed_msec, >=, 4000);
g_assert_cmpint (elapsed_msec, <, 5000);
g_print (" ");
}
g_main_loop_quit (loop);
}
static void
on_proxy_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
}
static void
test_method_calls_in_thread (void)
{
guint watcher_id;
watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION,
"com.example.TestService",
G_BUS_NAME_WATCHER_FLAGS_NONE,
"/com/example/TestObject",
"com.example.Frob",
G_TYPE_DBUS_PROXY,
G_DBUS_PROXY_FLAGS_NONE,
on_proxy_appeared,
on_proxy_vanished,
NULL,
NULL);
g_main_loop_run (loop);
g_bus_unwatch_proxy (watcher_id);
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
GError *error;
gint ret;
g_type_init ();
g_thread_init (NULL);
g_test_init (&argc, &argv, NULL);
/* all the tests rely on a shared main loop */
loop = g_main_loop_new (NULL, FALSE);
/* all the tests use a session bus with a well-known address that we can bring up and down
* using session_bus_up() and session_bus_down().
*/
g_unsetenv ("DISPLAY");
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
session_bus_up ();
/* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
* until one can connect to the bus but that's not how things work right now
*/
usleep (500 * 1000);
/* this is safe; testserver will exit once the bus goes away */
g_assert (g_spawn_command_line_async ("./gdbus-testserver.py", NULL));
/* wait for the service to come up */
usleep (500 * 1000);
/* Create the connection in the main thread */
error = NULL;
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (c != NULL);
g_test_add_func ("/gdbus/delivery-in-thread", test_delivery_in_thread);
g_test_add_func ("/gdbus/method-calls-in-thread", test_method_calls_in_thread);
ret = g_test_run();
g_object_unref (c);
/* tear down bus */
session_bus_down ();
return ret;
}