glib/gio/gunixconnection.c
Dan Winship 53beca955e Add GCancellables to GSocket ops
Currently, to implement cancellability correctly, all synchronous
calls to GSocket must be preceded by a g_socket_condition_wait() call,
(even though GSocket does this internally as well) and all
asynchronous calls must do occasional manual
g_cancellable_is_cancelled() checks. Since it's trivial to do these
checks inside GSocket instead, and we don't particularly want to
encourage people to use the APIs non-cancellably, move the
cancellation support into GSocket and simplify the existing callers.

http://bugzilla.gnome.org/show_bug.cgi?id=586797
2009-06-30 11:42:17 -04:00

292 lines
12 KiB
C

/* GIO - GLib Input, Output and Streaming Library
*
* Copyright © 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: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gunixconnection.h"
#include "glibintl.h"
/**
* SECTION: gunixconnection
* @title: GUnixConnection
* @short_description: a Unix domain #GSocketConnection
* @see_also: #GSocketConnection.
*
* This is the subclass of #GSocketConnection that is created
* for UNIX domain sockets.
*
* It contains functions to do some of the unix socket specific
* functionallity like passing file descriptors.
*
* Since: 2.22
*/
#include <gio/gsocketcontrolmessage.h>
#include <gio/gunixfdmessage.h>
#include <gio/gsocket.h>
#include <unistd.h>
#include "gioalias.h"
G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection,
G_TYPE_SOCKET_CONNECTION,
g_socket_connection_factory_register_type (g_define_type_id,
G_SOCKET_FAMILY_UNIX,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT);
);
/**
* g_unix_connection_send_fd:
* @connection: a #GUnixConnection
* @fd: a file descriptor
* @cancellable: optional #GCancellable object, %NULL to ignore.
* @error: #GError for error reporting, or %NULL to ignore.
*
* Passes a file descriptor to the recieving side of the
* connection. The recieving end has to call g_unix_connection_receive_fd()
* to accept the file descriptor.
*
* As well as sending the fd this also writes a single byte to the
* stream, as this is required for fd passing to work on some
* implementations.
*
* Returns: a %TRUE on success, %NULL on error.
*
* Since: 2.22
*/
gboolean
g_unix_connection_send_fd (GUnixConnection *connection,
gint fd,
GCancellable *cancellable,
GError **error)
{
GSocketControlMessage *scm;
GSocket *socket;
g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE);
g_return_val_if_fail (fd >= 0, FALSE);
scm = g_unix_fd_message_new ();
if (!g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (scm), fd, error))
{
g_object_unref (scm);
return FALSE;
}
g_object_get (connection, "socket", &socket, NULL);
if (g_socket_send_message (socket, NULL, NULL, 0, &scm, 1, 0, cancellable, error) != 1)
/* XXX could it 'fail' with zero? */
{
g_object_unref (socket);
g_object_unref (scm);
return FALSE;
}
g_object_unref (socket);
g_object_unref (scm);
return TRUE;
}
/**
* g_unix_connection_receive_fd:
* @connection: a #GUnixConnection
* @cancellable: optional #GCancellable object, %NULL to ignore
* @error: #GError for error reporting, or %NULL to ignore
*
* Receives a file descriptor from the sending end of the connection.
* The sending end has to call g_unix_connection_send_fd() for this
* to work.
*
* As well as reading the fd this also reads a single byte from the
* stream, as this is required for fd passing to work on some
* implementations.
*
* Returns: a file descriptor on success, -1 on error.
*
* Since: 2.22
*/
gint
g_unix_connection_receive_fd (GUnixConnection *connection,
GCancellable *cancellable,
GError **error)
{
GSocketControlMessage **scms;
gint *fds, nfd, fd, nscm;
GUnixFDMessage *fdmsg;
GSocket *socket;
g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), -1);
g_object_get (connection, "socket", &socket, NULL);
if (g_socket_receive_message (socket, NULL, NULL, 0,
&scms, &nscm, NULL, cancellable, error) != 1)
/* XXX it _could_ 'fail' with zero. */
{
g_object_unref (socket);
return -1;
}
g_object_unref (socket);
if (nscm != 1)
{
gint i;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Expecting 1 control message, got %d"), nscm);
for (i = 0; i < nscm; i++)
g_object_unref (scms[i]);
g_free (scms);
return -1;
}
if (!G_IS_UNIX_FD_MESSAGE (scms[0]))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Unexpected type of ancillary data"));
g_object_unref (scms[0]);
g_free (scms);
return -1;
}
fdmsg = G_UNIX_FD_MESSAGE (scms[0]);
g_free (scms);
fds = g_unix_fd_message_steal_fds (fdmsg, &nfd);
g_object_unref (fdmsg);
if (nfd != 1)
{
gint i;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Expecting one fd, but got %d\n"), nfd);
for (i = 0; i < nfd; i++)
close (fds[i]);
g_free (fds);
return -1;
}
fd = *fds;
g_free (fds);
if (fd < 0)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Received invalid fd"));
fd = -1;
}
return fd;
}
static void
g_unix_connection_init (GUnixConnection *connection)
{
}
static void
g_unix_connection_class_init (GUnixConnectionClass *class)
{
}
/* TODO: Other stuff we might want to add are:
void g_unix_connection_send_fd_async (GUnixConnection *connection,
gint fd,
gboolean close,
gint io_priority,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean g_unix_connection_send_fd_finish (GUnixConnection *connection,
GError **error);
gboolean g_unix_connection_send_fds (GUnixConnection *connection,
gint *fds,
gint nfds,
GError **error);
void g_unix_connection_send_fds_async (GUnixConnection *connection,
gint *fds,
gint nfds,
gint io_priority,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean g_unix_connection_send_fds_finish (GUnixConnection *connection,
GError **error);
void g_unix_connection_receive_fd_async (GUnixConnection *connection,
gint io_priority,
GAsyncReadyCallback callback,
gpointer user_data);
gint g_unix_connection_receive_fd_finish (GUnixConnection *connection,
GError **error);
gboolean g_unix_connection_send_credentials (GUnixConnection *connection,
GError **error);
void g_unix_connection_send_credentials_async (GUnixConnection *connection,
gint io_priority,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean g_unix_connection_send_credentials_finish (GUnixConnection *connection,
GError **error);
gboolean g_unix_connection_send_fake_credentials (GUnixConnection *connection,
guint64 pid,
guint64 uid,
guint64 gid,
GError **error);
void g_unix_connection_send_fake_credentials_async (GUnixConnection *connection,
guint64 pid,
guint64 uid,
guint64 gid,
gint io_priority,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean g_unix_connection_send_fake_credentials_finish (GUnixConnection *connection,
GError **error);
gboolean g_unix_connection_receive_credentials (GUnixConnection *connection,
guint64 *pid,
guint64 *uid,
guint64 *gid,
GError **error);
void g_unix_connection_receive_credentials_async (GUnixConnection *connection,
gint io_priority,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean g_unix_connection_receive_credentials_finish (GUnixConnection *connection,
guint64 *pid,
guint64 *uid,
guint64 *gid,
GError **error);
gboolean g_unix_connection_create_pair (GUnixConnection **one,
GUnixConnection **two,
GError **error);
*/
#define __G_UNIX_CONNECTION_C__
#include "gioaliasdef.c"