* 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.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
#include "giotypes.h"
#include "gsocket.h"
#include "ginputstream.h"
#include "gmemoryinputstream.h"
#include "giostream.h"
+#include "glib/gstdio.h"
#include "gsocketcontrolmessage.h"
#include "gsocketconnection.h"
#include "gsocketoutputstream.h"
#ifdef G_OS_UNIX
+#include "gkdbus.h"
+#include "gkdbusconnection.h"
#include "gunixfdmessage.h"
#include "gunixconnection.h"
#include "gunixcredentialsmessage.h"
/* ---------------------------------------------------------------------------------------------------- */
+#if defined (G_OS_UNIX) && (KDBUS_TRANSPORT)
+typedef struct
+{
+ GKdbus *kdbus;
+ GCancellable *cancellable;
+
+ GSimpleAsyncResult *simple;
+
+ gboolean from_mainloop;
+} ReadKdbusData;
+
+static void
+read_kdbus_data_free (ReadKdbusData *data)
+{
+ g_object_unref (data->kdbus);
+ if (data->cancellable != NULL)
+ g_object_unref (data->cancellable);
+ g_object_unref (data->simple);
+ g_free (data);
+}
+
+static gboolean
+_g_kdbus_read_ready (GKdbus *kdbus,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ ReadKdbusData *data = user_data;
+ GError *error = NULL;
+ gssize result;
+
+ result = _g_kdbus_receive (data->kdbus,
+ data->cancellable,
+ &error);
+
+ if (result >= 0)
+ {
+ g_simple_async_result_set_op_res_gssize (data->simple, result);
+ }
+ else
+ {
+ g_assert (error != NULL);
+ g_simple_async_result_take_error (data->simple, error);
+ }
+
+ if (data->from_mainloop)
+ g_simple_async_result_complete (data->simple);
+ else
+ g_simple_async_result_complete_in_idle (data->simple);
+
+ return FALSE;
+}
+
+static void
+_g_kdbus_read (GKdbus *kdbus,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ReadKdbusData *data;
+ GSource *source;
+
+ data = g_new0 (ReadKdbusData, 1);
+ data->kdbus = g_object_ref (kdbus);
+ data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
+
+ data->simple = g_simple_async_result_new (G_OBJECT (kdbus),
+ callback,
+ user_data,
+ _g_kdbus_read);
+ g_simple_async_result_set_check_cancellable (data->simple, cancellable);
+
+ data->from_mainloop = TRUE;
+ source = _g_kdbus_create_source (data->kdbus,
+ G_IO_IN,
+ cancellable);
+ g_source_set_callback (source,
+ (GSourceFunc) _g_kdbus_read_ready,
+ data,
+ (GDestroyNotify) read_kdbus_data_free);
+ g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+}
+
+static gssize
+_g_kdbus_read_finish (GKdbus *kdbus,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ g_return_val_if_fail (G_IS_KDBUS (kdbus), -1);
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _g_kdbus_read);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return -1;
+ else
+ return g_simple_async_result_get_op_res_gssize (simple);
+}
+
+#endif /* defined (G_OS_UNIX) && (KDBUS_TRANSPORT) */
+
/* Unfortunately ancillary messages are discarded when reading from a
* socket using the GSocketInputStream abstraction. So we provide a
* very GInputStream-ish API that uses GSocket in this case (very
callback,
user_data,
_g_socket_read_with_control_messages);
+ g_simple_async_result_set_check_cancellable (data->simple, cancellable);
if (!g_socket_condition_check (socket, G_IO_IN))
{
GDBusWorkerDisconnectedCallback disconnected_callback;
gpointer user_data;
- /* if not NULL, stream is GSocketConnection */
+ /* if GSocket and GKdbus are NULL, stream is GSocketConnection */
GSocket *socket;
+#if defined (G_OS_UNIX) && (KDBUS_TRANSPORT)
+ GKdbus *kdbus;
+#endif
/* used for reading */
GMutex read_lock;
GQueue *write_queue;
/* protected by write_lock */
guint64 write_num_messages_written;
+ /* number of messages we'd written out last time we flushed;
+ * protected by write_lock
+ */
+ guint64 write_num_messages_flushed;
/* list of FlushData, protected by write_lock */
GList *write_pending_flushes;
/* list of CloseData, protected by write_lock */
if (worker->read_fd_list != NULL)
g_object_unref (worker->read_fd_list);
- g_queue_foreach (worker->received_messages_while_frozen, (GFunc) g_object_unref, NULL);
- g_queue_free (worker->received_messages_while_frozen);
-
+ g_queue_free_full (worker->received_messages_while_frozen, (GDestroyNotify) g_object_unref);
g_mutex_clear (&worker->write_lock);
- g_queue_foreach (worker->write_queue, (GFunc) message_to_write_data_free, NULL);
- g_queue_free (worker->write_queue);
-
+ g_queue_free_full (worker->write_queue, (GDestroyNotify) message_to_write_data_free);
g_free (worker->read_buffer);
g_free (worker);
unfreeze_in_idle_cb,
_g_dbus_worker_ref (worker),
(GDestroyNotify) _g_dbus_worker_unref);
+ g_source_set_name (idle_source, "[gio] unfreeze_in_idle_cb");
g_source_attach (idle_source, worker->shared_thread_data->context);
g_source_unref (idle_source);
}
goto out;
error = NULL;
- if (worker->socket == NULL)
+ bytes_read = 0;
+
+ if (FALSE)
+ {
+ }
+#if defined (G_OS_UNIX) && (KDBUS_TRANSPORT)
+ else if (G_IS_KDBUS_CONNECTION (worker->stream))
+ {
+ bytes_read = _g_kdbus_read_finish (worker->kdbus,
+ res,
+ &error);
+ g_error ("[KDBUS] _g_dbus_worker_do_read_cb() - work in progress");
+ }
+#endif
+ else if (worker->socket == NULL)
bytes_read = g_input_stream_read_finish (g_io_stream_get_input_stream (worker->stream),
res,
&error);
{
/* TODO: really want a append_steal() */
g_unix_fd_list_append (worker->read_fd_list, fds[n], NULL);
- close (fds[n]);
+ (void) g_close (fds[n], NULL);
}
}
g_free (fds);
&error);
if (message_len == -1)
{
- g_warning ("_g_dbus_worker_do_read_cb: error determing bytes needed: %s", error->message);
+ g_warning ("_g_dbus_worker_do_read_cb: error determining bytes needed: %s", error->message);
_g_dbus_worker_emit_disconnected (worker, FALSE, error);
g_error_free (error);
goto out;
* true, because only failing a read causes us to signal 'closed'.
*/
+ /* [KDBUS]
+ * For KDBUS transport we don't have to alloc buffer (worker->read_buffer)
+ * instead of it we use kdbus memory pool. On connection stage KDBUS client
+ * have to register a memory pool, large enough to carry all backlog of
+ * data enqueued for the connection.
+ */
+
+#if defined (G_OS_UNIX) && (KDBUS_TRANSPORT)
+ if (G_IS_KDBUS_CONNECTION (worker->stream))
+ {
+ _g_kdbus_read(worker->kdbus,
+ worker->cancellable,
+ (GAsyncReadyCallback) _g_dbus_worker_do_read_cb,
+ _g_dbus_worker_ref (worker));
+ return;
+ }
+#endif
+
/* if bytes_wanted is zero, it means start reading a message */
if (worker->read_buffer_bytes_wanted == 0)
{
* write-lock is not held on entry
* output_pending is PENDING_WRITE on entry
*/
+#ifdef G_OS_UNIX
static gboolean
on_socket_ready (GSocket *socket,
GIOCondition condition,
write_message_continue_writing (data);
return FALSE; /* remove source */
}
+#endif
/* called in private thread shared by all GDBusConnection instances
*
write_message_continue_writing (MessageToWriteData *data)
{
GOutputStream *ostream;
- GSimpleAsyncResult *simple;
#ifdef G_OS_UNIX
+ GSimpleAsyncResult *simple;
GUnixFDList *fd_list;
#endif
+#ifdef G_OS_UNIX
/* Note: we can't access data->simple after calling g_async_result_complete () because the
* callback can free @data and we're not completing in idle. So use a copy of the pointer.
*/
simple = data->simple;
+#endif
ostream = g_io_stream_get_output_stream (data->worker->stream);
#ifdef G_OS_UNIX
write_message_async_cb,
data);
}
+#ifdef G_OS_UNIX
out:
+#endif
;
}
/* Make sure we tell folks that we don't have additional
flushes pending */
g_mutex_lock (&data->worker->write_lock);
+ data->worker->write_num_messages_flushed = data->worker->write_num_messages_written;
g_assert (data->worker->output_pending == PENDING_FLUSH);
data->worker->output_pending = PENDING_NONE;
g_mutex_unlock (&data->worker->write_lock);
g_clear_error (&error);
/* all messages queued for sending are discarded */
- g_queue_foreach (send_queue, (GFunc) message_to_write_data_free, NULL);
- g_queue_free (send_queue);
-
+ g_queue_free_full (send_queue, (GDestroyNotify) message_to_write_data_free);
/* all queued flushes fail */
error = g_error_new (G_IO_ERROR, G_IO_ERROR_CANCELLED,
_("Operation was cancelled"));
g_mutex_lock (&worker->write_lock);
+ data = NULL;
+ flush_async_data = NULL;
+
/* if we want to close the connection, that takes precedence */
if (worker->pending_close_attempts != NULL)
{
g_io_stream_close_async (worker->stream, G_PRIORITY_DEFAULT,
NULL, iostream_close_cb,
_g_dbus_worker_ref (worker));
- data = NULL;
}
else
{
if (data != NULL)
worker->output_pending = PENDING_WRITE;
}
- else
- {
- data = NULL;
- }
}
g_mutex_unlock (&worker->write_lock);
return FALSE;
}
-/*
+/**
* @write_data: (transfer full) (allow-none):
+ * @flush_data: (transfer full) (allow-none):
* @close_data: (transfer full) (allow-none):
*
* Can be called from any thread
static void
schedule_writing_unlocked (GDBusWorker *worker,
MessageToWriteData *write_data,
+ FlushData *flush_data,
CloseData *close_data)
{
if (write_data != NULL)
g_queue_push_tail (worker->write_queue, write_data);
+ if (flush_data != NULL)
+ worker->write_pending_flushes = g_list_prepend (worker->write_pending_flushes, flush_data);
+
if (close_data != NULL)
worker->pending_close_attempts = g_list_prepend (worker->pending_close_attempts,
close_data);
+ /* If we had output pending, the next bit of output will happen
+ * automatically when it finishes, so we only need to do this
+ * if nothing was pending.
+ *
+ * The idle callback will re-check that output_pending is still
+ * PENDING_NONE, to guard against output starting before the idle.
+ */
if (worker->output_pending == PENDING_NONE)
{
GSource *idle_source;
continue_writing_in_idle_cb,
_g_dbus_worker_ref (worker),
(GDestroyNotify) _g_dbus_worker_unref);
+ g_source_set_name (idle_source, "[gio] continue_writing_in_idle_cb");
g_source_attach (idle_source, worker->shared_thread_data->context);
g_source_unref (idle_source);
}
data->blob_size = blob_len;
g_mutex_lock (&worker->write_lock);
- schedule_writing_unlocked (worker, data, NULL);
+ schedule_writing_unlocked (worker, data, NULL, NULL);
g_mutex_unlock (&worker->write_lock);
}
if (G_IS_SOCKET_CONNECTION (worker->stream))
worker->socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream));
+#if defined (G_OS_UNIX) && (KDBUS_TRANSPORT)
+ if (G_IS_KDBUS_CONNECTION (worker->stream))
+ worker->kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (worker->stream));
+#endif
+
worker->shared_thread_data = _g_dbus_shared_thread_ref ();
/* begin reading */
_g_dbus_worker_do_initial_read,
_g_dbus_worker_ref (worker),
(GDestroyNotify) _g_dbus_worker_unref);
+ g_source_set_name (idle_source, "[gio] _g_dbus_worker_do_initial_read");
g_source_attach (idle_source, worker->shared_thread_data->context);
g_source_unref (idle_source);
*/
g_cancellable_cancel (worker->cancellable);
g_mutex_lock (&worker->write_lock);
- schedule_writing_unlocked (worker, NULL, close_data);
+ schedule_writing_unlocked (worker, NULL, NULL, close_data);
g_mutex_unlock (&worker->write_lock);
}
*/
_g_dbus_worker_close (worker, NULL, NULL);
- /* _g_dbus_worker_close holds a ref until after an idle in the the worker
+ /* _g_dbus_worker_close holds a ref until after an idle in the worker
* thread has run, so we no longer need to unref in an idle like in
* commit 322e25b535
*/
{
gboolean ret;
FlushData *data;
+ guint64 pending_writes;
data = NULL;
ret = TRUE;
- /* if the queue is empty, there's nothing to wait for */
g_mutex_lock (&worker->write_lock);
- if (g_queue_get_length (worker->write_queue) > 0)
+
+ /* if the queue is empty, no write is in-flight and we haven't written
+ * anything since the last flush, then there's nothing to wait for
+ */
+ pending_writes = g_queue_get_length (worker->write_queue);
+
+ /* if a write is in-flight, we shouldn't be satisfied until the first
+ * flush operation that follows it
+ */
+ if (worker->output_pending == PENDING_WRITE)
+ pending_writes += 1;
+
+ if (pending_writes > 0 ||
+ worker->write_num_messages_written != worker->write_num_messages_flushed)
{
data = g_new0 (FlushData, 1);
g_mutex_init (&data->mutex);
g_cond_init (&data->cond);
- data->number_to_wait_for = worker->write_num_messages_written + g_queue_get_length (worker->write_queue);
+ data->number_to_wait_for = worker->write_num_messages_written + pending_writes;
g_mutex_lock (&data->mutex);
- worker->write_pending_flushes = g_list_prepend (worker->write_pending_flushes, data);
+
+ schedule_writing_unlocked (worker, NULL, data, NULL);
}
g_mutex_unlock (&worker->write_lock);
G_UNLOCK (print_lock);
}
-/*
+/**
* _g_dbus_initialize:
*
* Does various one-time init things such as
gchar *
_g_dbus_get_machine_id (GError **error)
{
+#ifdef G_OS_WIN32
+ HW_PROFILE_INFOA info;
+ char *src, *dest, *res;
+ int i;
+
+ if (!GetCurrentHwProfileA (&info))
+ {
+ char *message = g_win32_error_message (GetLastError ());
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Unable to get Hardware profile: %s"), message);
+ g_free (message);
+ return NULL;
+ }
+
+ /* Form: {12340001-4980-1920-6788-123456789012} */
+ src = &info.szHwProfileGuid[0];
+
+ res = g_malloc (32+1);
+ dest = res;
+
+ src++; /* Skip { */
+ for (i = 0; i < 8; i++)
+ *dest++ = *src++;
+ src++; /* Skip - */
+ for (i = 0; i < 4; i++)
+ *dest++ = *src++;
+ src++; /* Skip - */
+ for (i = 0; i < 4; i++)
+ *dest++ = *src++;
+ src++; /* Skip - */
+ for (i = 0; i < 4; i++)
+ *dest++ = *src++;
+ src++; /* Skip - */
+ for (i = 0; i < 12; i++)
+ *dest++ = *src++;
+ *dest = 0;
+
+ return res;
+#else
gchar *ret;
GError *first_error;
/* TODO: use PACKAGE_LOCALSTATEDIR ? */
g_strstrip (ret);
}
return ret;
+#endif
}
/* ---------------------------------------------------------------------------------------------------- */
_g_dbus_debug_print_lock ();
g_print ("========================================================================\n"
"GDBus-debug:Transport:\n"
- " >>>> WROTE %" G_GSIZE_FORMAT " bytes of message with serial %d and\n"
+ " >>>> WROTE %" G_GSSIZE_FORMAT " bytes of message with serial %d and\n"
" size %" G_GSIZE_FORMAT " from offset %" G_GSIZE_FORMAT " on a %s\n",
bytes_written,
g_dbus_message_get_serial (data->message),
_g_dbus_debug_print_lock ();
g_print ("========================================================================\n"
"GDBus-debug:Transport:\n"
- " <<<< READ %" G_GSIZE_FORMAT " bytes of message with serial %d and\n"
+ " <<<< READ %" G_GSSIZE_FORMAT " bytes of message with serial %d and\n"
" size %d to offset %" G_GSIZE_FORMAT " from a %s\n",
bytes_read,
serial,