X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgunixconnection.c;h=b5da60a4edeb082469e5adc99d0a0a9e606f3a25;hb=f14a66e3df9e5e3f0f170b68e976011c80ffc041;hp=3458aa168ad681e480fb4099b032b489e3daeaa3;hpb=80cfd099f3c9fa23b2a21c77e3698f1c4ac94b06;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gunixconnection.c b/gio/gunixconnection.c index 3458aa1..b5da60a 100644 --- a/gio/gunixconnection.c +++ b/gio/gunixconnection.c @@ -13,30 +13,38 @@ */ #include "config.h" + #include "gunixconnection.h" +#include "gnetworking.h" +#include "gsocket.h" +#include "gsocketcontrolmessage.h" +#include "gunixcredentialsmessage.h" +#include "gunixfdmessage.h" #include "glibintl.h" +#include +#include +#include + /** - * SECTION: gunixconnection + * SECTION:gunixconnection * @title: GUnixConnection - * @short_description: a Unix domain #GSocketConnection + * @short_description: A UNIX domain GSocketConnection + * @include: gio/gunixconnection.h * @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. + * It contains functions to do some of the UNIX socket specific + * functionality like passing file descriptors. + * + * Note that `` belongs to the UNIX-specific + * GIO interfaces, thus you have to use the `gio-unix-2.0.pc` + * pkg-config file when using it. * * Since: 2.22 - **/ - -#include -#include -#include -#include - -#include "gioalias.h" + */ G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection, G_TYPE_SOCKET_CONNECTION, @@ -48,13 +56,13 @@ G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection, /** * g_unix_connection_send_fd: - * @connection: a #GUnixConnection. + * @connection: a #GUnixConnection * @fd: a file descriptor - * @cancellable: optional #GCancellable object, %NULL to ignore. - * @error: #GError for error reporting, or %NULL to ignore. + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore. + * @error: (allow-none): #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() + * Passes a file descriptor to the receiving side of the + * connection. The receiving 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 @@ -64,7 +72,7 @@ G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection, * Returns: a %TRUE on success, %NULL on error. * * Since: 2.22 - **/ + */ gboolean g_unix_connection_send_fd (GUnixConnection *connection, gint fd, @@ -86,8 +94,7 @@ g_unix_connection_send_fd (GUnixConnection *connection, } g_object_get (connection, "socket", &socket, NULL); - if (!g_socket_condition_wait (socket, G_IO_OUT, cancellable, error) || - g_socket_send_message (socket, NULL, NULL, 0, &scm, 1, 0, error) != 1) + 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); @@ -104,13 +111,13 @@ g_unix_connection_send_fd (GUnixConnection *connection, /** * g_unix_connection_receive_fd: - * @connection: a #GUnixConnection. - * @cancellable: optional #GCancellable object, %NULL to ignore. - * @error: #GError for error reporting, or %NULL to ignore. + * @connection: a #GUnixConnection + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore + * @error: (allow-none): #GError for error reporting, or %NULL to ignore * - * Recieves 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. + * 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 @@ -133,9 +140,8 @@ g_unix_connection_receive_fd (GUnixConnection *connection, g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), -1); g_object_get (connection, "socket", &socket, NULL); - if (!g_socket_condition_wait (socket, G_IO_IN, cancellable, error) || - g_socket_receive_message (socket, NULL, NULL, 0, - &scms, &nscm, NULL, error) != 1) + 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); @@ -150,7 +156,10 @@ g_unix_connection_receive_fd (GUnixConnection *connection, gint i; g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Expecting 1 control message, got %d"), nscm); + ngettext("Expecting 1 control message, got %d", + "Expecting 1 control message, got %d", + nscm), + nscm); for (i = 0; i < nscm; i++) g_object_unref (scms[i]); @@ -181,7 +190,10 @@ g_unix_connection_receive_fd (GUnixConnection *connection, gint i; g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Expecting one fd, but got %d\n"), nfd); + ngettext("Expecting one fd, but got %d\n", + "Expecting one fd, but got %d\n", + nfd), + nfd); for (i = 0; i < nfd; i++) close (fds[i]); @@ -245,15 +257,6 @@ gint g_unix_connection_receive_fd_finish (GUnixCo 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, @@ -269,25 +272,421 @@ void g_unix_connection_send_fake_credentials_async (GUnixCo 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" + +/** + * g_unix_connection_send_credentials: + * @connection: A #GUnixConnection. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Passes the credentials of the current user the receiving side + * of the connection. The receiving end has to call + * g_unix_connection_receive_credentials() (or similar) to accept the + * credentials. + * + * As well as sending the credentials this also writes a single NUL + * byte to the stream, as this is required for credentials passing to + * work on some implementations. + * + * Other ways to exchange credentials with a foreign peer includes the + * #GUnixCredentialsMessage type and g_socket_get_credentials() function. + * + * Returns: %TRUE on success, %FALSE if @error is set. + * + * Since: 2.26 + */ +gboolean +g_unix_connection_send_credentials (GUnixConnection *connection, + GCancellable *cancellable, + GError **error) +{ + GCredentials *credentials; + GSocketControlMessage *scm; + GSocket *socket; + gboolean ret; + GOutputVector vector; + guchar nul_byte[1] = {'\0'}; + gint num_messages; + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; + + credentials = g_credentials_new (); + + vector.buffer = &nul_byte; + vector.size = 1; + + if (g_unix_credentials_message_is_supported ()) + { + scm = g_unix_credentials_message_new_with_credentials (credentials); + num_messages = 1; + } + else + { + scm = NULL; + num_messages = 0; + } + + g_object_get (connection, "socket", &socket, NULL); + if (g_socket_send_message (socket, + NULL, /* address */ + &vector, + 1, + &scm, + num_messages, + G_SOCKET_MSG_NONE, + cancellable, + error) != 1) + { + g_prefix_error (error, _("Error sending credentials: ")); + goto out; + } + + ret = TRUE; + + out: + g_object_unref (socket); + if (scm != NULL) + g_object_unref (scm); + g_object_unref (credentials); + return ret; +} + +static void +send_credentials_async_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GError *error = NULL; + + if (g_unix_connection_send_credentials (G_UNIX_CONNECTION (source_object), + cancellable, + &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); + g_object_unref (task); +} + +/** + * g_unix_connection_send_credentials_async: + * @connection: A #GUnixConnection. + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore. + * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: (closure): the data to pass to callback function + * + * Asynchronously send credentials. + * + * For more details, see g_unix_connection_send_credentials() which is + * the synchronous version of this call. + * + * When the operation is finished, @callback will be called. You can then call + * g_unix_connection_send_credentials_finish() to get the result of the operation. + * + * Since: 2.32 + **/ +void +g_unix_connection_send_credentials_async (GUnixConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (connection, cancellable, callback, user_data); + + g_task_run_in_thread (task, send_credentials_async_thread); +} + +/** + * g_unix_connection_send_credentials_finish: + * @connection: A #GUnixConnection. + * @result: a #GAsyncResult. + * @error: a #GError, or %NULL + * + * Finishes an asynchronous send credentials operation started with + * g_unix_connection_send_credentials_async(). + * + * Returns: %TRUE if the operation was successful, otherwise %FALSE. + * + * Since: 2.32 + **/ +gboolean +g_unix_connection_send_credentials_finish (GUnixConnection *connection, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, connection), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +/** + * g_unix_connection_receive_credentials: + * @connection: A #GUnixConnection. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Receives credentials from the sending end of the connection. The + * sending end has to call g_unix_connection_send_credentials() (or + * similar) for this to work. + * + * As well as reading the credentials this also reads (and discards) a + * single byte from the stream, as this is required for credentials + * passing to work on some implementations. + * + * Other ways to exchange credentials with a foreign peer includes the + * #GUnixCredentialsMessage type and g_socket_get_credentials() function. + * + * Returns: (transfer full): Received credentials on success (free with + * g_object_unref()), %NULL if @error is set. + * + * Since: 2.26 + */ +GCredentials * +g_unix_connection_receive_credentials (GUnixConnection *connection, + GCancellable *cancellable, + GError **error) +{ + GCredentials *ret; + GSocketControlMessage **scms; + gint nscm; + GSocket *socket; + gint n; + gssize num_bytes_read; +#ifdef __linux__ + gboolean turn_off_so_passcreds; +#endif + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + ret = NULL; + scms = NULL; + + g_object_get (connection, "socket", &socket, NULL); + + /* On Linux, we need to turn on SO_PASSCRED if it isn't enabled + * already. We also need to turn it off when we're done. See + * #617483 for more discussion. + */ +#ifdef __linux__ + { + gint opt_val; + + turn_off_so_passcreds = FALSE; + opt_val = 0; + if (!g_socket_get_option (socket, + SOL_SOCKET, + SO_PASSCRED, + &opt_val, + NULL)) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error checking if SO_PASSCRED is enabled for socket: %s"), + strerror (errno)); + goto out; + } + if (opt_val == 0) + { + if (!g_socket_set_option (socket, + SOL_SOCKET, + SO_PASSCRED, + TRUE, + NULL)) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error enabling SO_PASSCRED: %s"), + strerror (errno)); + goto out; + } + turn_off_so_passcreds = TRUE; + } + } +#endif + + g_type_ensure (G_TYPE_UNIX_CREDENTIALS_MESSAGE); + num_bytes_read = g_socket_receive_message (socket, + NULL, /* GSocketAddress **address */ + NULL, + 0, + &scms, + &nscm, + NULL, + cancellable, + error); + if (num_bytes_read != 1) + { + /* Handle situation where g_socket_receive_message() returns + * 0 bytes and not setting @error + */ + if (num_bytes_read == 0 && error != NULL && *error == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Expecting to read a single byte for receiving credentials but read zero bytes")); + } + goto out; + } + + if (g_unix_credentials_message_is_supported () && + /* Fall back on get_credentials if the other side didn't send the credentials */ + nscm > 0) + { + if (nscm != 1) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + ngettext("Expecting 1 control message, got %d", + "Expecting 1 control message, got %d", + nscm), + nscm); + goto out; + } + + if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0])) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected type of ancillary data")); + goto out; + } + + ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0])); + g_object_ref (ret); + } + else + { + if (nscm != 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Not expecting control message, but got %d"), + nscm); + goto out; + } + else + { + ret = g_socket_get_credentials (socket, error); + } + } + + out: + +#ifdef __linux__ + if (turn_off_so_passcreds) + { + if (!g_socket_set_option (socket, + SOL_SOCKET, + SO_PASSCRED, + FALSE, + NULL)) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error while disabling SO_PASSCRED: %s"), + strerror (errno)); + goto out; + } + } +#endif + + if (scms != NULL) + { + for (n = 0; n < nscm; n++) + g_object_unref (scms[n]); + g_free (scms); + } + g_object_unref (socket); + return ret; +} + +static void +receive_credentials_async_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GCredentials *creds; + GError *error = NULL; + + creds = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (source_object), + cancellable, + &error); + if (creds) + g_task_return_pointer (task, creds, g_object_unref); + else + g_task_return_error (task, error); + g_object_unref (task); +} + +/** + * g_unix_connection_receive_credentials_async: + * @connection: A #GUnixConnection. + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore. + * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: (closure): the data to pass to callback function + * + * Asynchronously receive credentials. + * + * For more details, see g_unix_connection_receive_credentials() which is + * the synchronous version of this call. + * + * When the operation is finished, @callback will be called. You can then call + * g_unix_connection_receive_credentials_finish() to get the result of the operation. + * + * Since: 2.32 + **/ +void +g_unix_connection_receive_credentials_async (GUnixConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (connection, cancellable, callback, user_data); + + g_task_run_in_thread (task, receive_credentials_async_thread); +} + +/** + * g_unix_connection_receive_credentials_finish: + * @connection: A #GUnixConnection. + * @result: a #GAsyncResult. + * @error: a #GError, or %NULL + * + * Finishes an asynchronous receive credentials operation started with + * g_unix_connection_receive_credentials_async(). + * + * Returns: (transfer full): a #GCredentials, or %NULL on error. + * Free the returned object with g_object_unref(). + * + * Since: 2.32 + **/ +GCredentials * +g_unix_connection_receive_credentials_finish (GUnixConnection *connection, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, connection), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +}