/*
* This file is part of Cockpit.
*
* Copyright (C) 2014 Red Hat, Inc.
*
* Cockpit 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.1 of the License, or
* (at your option) any later version.
*
* Cockpit 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 Cockpit; If not, see .
*/
#include "config.h"
#include "cockpitdbusinternal.h"
#include
#include
#include
#include
#include
typedef struct {
GIOStream parent;
gint fd;
GInputStream *input_stream;
GOutputStream *output_stream;
} UnixIOStream;
typedef struct _GIOStreamClass UnixIOStreamClass;
GType unix_io_stream_get_type (void);
G_DEFINE_TYPE (UnixIOStream, unix_io_stream, G_TYPE_IO_STREAM)
static void
unix_io_stream_finalize (GObject *object)
{
UnixIOStream *self = (UnixIOStream *)object;
/* strictly speaking we should unref these in dispose, but
* g_io_stream_dispose() wants them to still exist
*/
g_clear_object (&self->input_stream);
g_clear_object (&self->output_stream);
G_OBJECT_CLASS (unix_io_stream_parent_class)->finalize (object);
}
static void
unix_io_stream_init (UnixIOStream *stream)
{
}
static GInputStream *
unix_io_stream_get_input_stream (GIOStream *stream)
{
UnixIOStream *self = (UnixIOStream *)stream;
return self->input_stream;
}
static GOutputStream *
unix_io_stream_get_output_stream (GIOStream *stream)
{
UnixIOStream *self = (UnixIOStream *)stream;
return self->output_stream;
}
static gboolean
unix_io_stream_close (GIOStream *stream,
GCancellable *cancellable,
GError **error)
{
UnixIOStream *self = (UnixIOStream *)stream;
gboolean ret;
ret = g_input_stream_close (self->input_stream, cancellable, error);
if (!g_output_stream_close (self->output_stream, cancellable, ret ? error : NULL))
ret = FALSE;
close (self->fd);
return ret;
}
static void
unix_io_stream_close_async (GIOStream *stream,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *res;
GError *error = NULL;
res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
unix_io_stream_close_async);
if (!unix_io_stream_close (stream, cancellable, &error))
g_simple_async_result_take_error (res, error);
g_simple_async_result_complete_in_idle (res);
g_object_unref (res);
}
static gboolean
unix_io_stream_close_finish (GIOStream *stream,
GAsyncResult *result,
GError **error)
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
return FALSE;
return TRUE;
}
static void
unix_io_stream_class_init (UnixIOStreamClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GIOStreamClass *stream_class = G_IO_STREAM_CLASS (klass);
gobject_class->finalize = unix_io_stream_finalize;
stream_class->get_input_stream = unix_io_stream_get_input_stream;
stream_class->get_output_stream = unix_io_stream_get_output_stream;
stream_class->close_fn = unix_io_stream_close;
stream_class->close_async = unix_io_stream_close_async;
stream_class->close_finish = unix_io_stream_close_finish;
}
static GIOStream *
unix_io_stream_new (gint fd)
{
UnixIOStream *self;
self = g_object_new (unix_io_stream_get_type (), NULL);
self->input_stream = g_unix_input_stream_new (fd, FALSE);
self->output_stream = g_unix_output_stream_new (fd, FALSE);
self->fd = fd;
return G_IO_STREAM (self);
}
/* ------------------------------------------------------------------------- */
static GDBusConnection *the_server = NULL;
static GDBusConnection *the_client = NULL;
const gchar *the_name = NULL;
GDBusConnection *
cockpit_dbus_internal_client (void)
{
g_return_val_if_fail (the_client != NULL, NULL);
return g_object_ref (the_client);
}
const gchar *
cockpit_dbus_internal_name (void)
{
return the_name;
}
GDBusConnection *
cockpit_dbus_internal_server (void)
{
g_return_val_if_fail (the_server != NULL, NULL);
return g_object_ref (the_server);
}
static void
on_complete_get_result (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GAsyncResult **ret = user_data;
g_assert (*ret == NULL);
*ret = g_object_ref (result);
}
void
cockpit_dbus_internal_startup (gboolean interact)
{
GAsyncResult *rclient = NULL;
GAsyncResult *rserver = NULL;
GError *error = NULL;
GIOStream *io;
gchar *guid;
int fds[2];
/*
* When in interactive mode, we allow poking and prodding our internal
* DBus interface. Therefore be on the session bus instead of peer-to-peer.
*/
if (interact)
{
the_server = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (the_server)
{
the_name = g_dbus_connection_get_unique_name (the_server);
the_client = g_object_ref (the_server);
return;
}
else
{
g_message ("couldn't connect to session bus: %s", error->message);
g_clear_error (&error);
}
}
if (socketpair (PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
{
g_warning ("couldn't create loopback socket: %s", g_strerror (errno));
return;
}
io = unix_io_stream_new (fds[0]);
guid = g_dbus_generate_guid ();
g_dbus_connection_new (io, guid,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER |
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS,
NULL, NULL, on_complete_get_result, &rserver);
g_object_unref (io);
io = unix_io_stream_new (fds[1]);
g_dbus_connection_new (io, NULL, G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
NULL, NULL, on_complete_get_result, &rclient);
g_object_unref (io);
while (!rserver || !rclient)
g_main_context_iteration (NULL, TRUE);
the_server = g_dbus_connection_new_finish (rserver, &error);
if (the_server == NULL)
{
g_warning ("couldn't create internal connection: %s", error->message);
g_clear_error (&error);
}
the_client = g_dbus_connection_new_finish (rclient, &error);
if (the_client == NULL)
{
g_warning ("couldn't create internal connection: %s", error->message);
g_clear_error (&error);
}
g_object_unref (rclient);
g_object_unref (rserver);
g_free (guid);
}
void
cockpit_dbus_internal_cleanup (void)
{
g_clear_object (&the_client);
g_clear_object (&the_server);
}