Silence a bunch of -Wunused-but-set-variable warnings
[platform/upstream/glib.git] / gio / gsocket.c
index 298f524..aa0a39c 100644 (file)
 #include "gioerror.h"
 #include "gioenums.h"
 #include "gioerror.h"
+#include "gio-marshal.h"
 #include "gnetworkingprivate.h"
 #include "gsocketaddress.h"
 #include "gsocketcontrolmessage.h"
+#include "gcredentials.h"
 #include "glibintl.h"
 
-
 /**
  * SECTION:gsocket
  * @short_description: Low-level socket object
@@ -145,6 +146,7 @@ struct _GSocketPrivate
   gint            listen_backlog;
   guint           timeout;
   GError         *construct_error;
+  GSocketAddress *remote_address;
   guint           inited : 1;
   guint           blocking : 1;
   guint           keepalive : 1;
@@ -152,6 +154,7 @@ struct _GSocketPrivate
   guint           connected : 1;
   guint           listening : 1;
   guint           timed_out : 1;
+  guint           connect_pending : 1;
 #ifdef G_OS_WIN32
   WSAEVENT        event;
   int             current_events;
@@ -375,13 +378,34 @@ g_socket_details_from_fd (GSocket *socket)
     {
      case G_SOCKET_FAMILY_IPV4:
      case G_SOCKET_FAMILY_IPV6:
+       socket->priv->family = address.ss_family;
+       switch (socket->priv->type)
+        {
+        case G_SOCKET_TYPE_STREAM:
+          socket->priv->protocol = G_SOCKET_PROTOCOL_TCP;
+          break;
+
+        case G_SOCKET_TYPE_DATAGRAM:
+          socket->priv->protocol = G_SOCKET_PROTOCOL_UDP;
+          break;
+
+        case G_SOCKET_TYPE_SEQPACKET:
+          socket->priv->protocol = G_SOCKET_PROTOCOL_SCTP;
+          break;
+
+        default:
+          break;
+        }
+       break;
+
      case G_SOCKET_FAMILY_UNIX:
-      socket->priv->family = address.ss_family;
-      break;
+       socket->priv->family = G_SOCKET_FAMILY_UNIX;
+       socket->priv->protocol = G_SOCKET_PROTOCOL_DEFAULT;
+       break;
 
      default:
-      socket->priv->family = G_SOCKET_FAMILY_INVALID;
-      break;
+       socket->priv->family = G_SOCKET_FAMILY_INVALID;
+       break;
     }
 
   if (socket->priv->family != G_SOCKET_FAMILY_INVALID)
@@ -455,9 +479,11 @@ g_socket_create_socket (GSocketFamily   family,
     }
 
 #ifdef SOCK_CLOEXEC
-  native_type |= SOCK_CLOEXEC;
+  fd = socket (family, native_type | SOCK_CLOEXEC, protocol);
+  /* It's possible that libc has SOCK_CLOEXEC but the kernel does not */
+  if (fd < 0 && errno == EINVAL)
 #endif
-  fd = socket (family, native_type, protocol);
+    fd = socket (family, native_type, protocol);
 
   if (fd < 0)
     {
@@ -627,6 +653,9 @@ g_socket_finalize (GObject *object)
       !socket->priv->closed)
     g_socket_close (socket, NULL);
 
+  if (socket->priv->remote_address)
+    g_object_unref (socket->priv->remote_address);
+
 #ifdef G_OS_WIN32
   if (socket->priv->event != WSA_INVALID_EVENT)
     {
@@ -649,6 +678,7 @@ g_socket_class_init (GSocketClass *klass)
 
   /* Make sure winsock has been initialized */
   type = g_inet_address_get_type ();
+  (type); /* To avoid -Wunused-but-set-variable */
 
 #ifdef SIGPIPE
   /* There is no portable, thread-safe way to avoid having the process
@@ -1196,7 +1226,7 @@ g_socket_get_fd (GSocket *socket)
  * useful if the socket has been bound to a local address,
  * either explicitly or implicitly when connecting.
  *
- * Returns: a #GSocketAddress or %NULL on error.
+ * Returns: (transfer full): a #GSocketAddress or %NULL on error.
  *     Free the returned object with g_object_unref().
  *
  * Since: 2.22
@@ -1229,7 +1259,7 @@ g_socket_get_local_address (GSocket  *socket,
  * Try to get the remove address of a connected socket. This is only
  * useful for connection oriented sockets that have been connected.
  *
- * Returns: a #GSocketAddress or %NULL on error.
+ * Returns: (transfer full): a #GSocketAddress or %NULL on error.
  *     Free the returned object with g_object_unref().
  *
  * Since: 2.22
@@ -1243,15 +1273,28 @@ g_socket_get_remote_address (GSocket  *socket,
 
   g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
 
-  if (getpeername (socket->priv->fd, (struct sockaddr *) &buffer, &len) < 0)
+  if (socket->priv->connect_pending)
     {
-      int errsv = get_socket_errno ();
-      g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
-                  _("could not get remote address: %s"), socket_strerror (errsv));
-      return NULL;
+      if (!g_socket_check_connect_result (socket, error))
+        return NULL;
+      else
+        socket->priv->connect_pending = FALSE;
     }
 
-  return g_socket_address_new_from_native (&buffer, len);
+  if (!socket->priv->remote_address)
+    {
+      if (getpeername (socket->priv->fd, (struct sockaddr *) &buffer, &len) < 0)
+       {
+         int errsv = get_socket_errno ();
+         g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+                      _("could not get remote address: %s"), socket_strerror (errsv));
+         return NULL;
+       }
+
+      socket->priv->remote_address = g_socket_address_new_from_native (&buffer, len);
+    }
+
+  return g_object_ref (socket->priv->remote_address);
 }
 
 /**
@@ -1437,7 +1480,7 @@ g_socket_speaks_ipv4 (GSocket *socket)
 /**
  * g_socket_accept:
  * @socket: a #GSocket.
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Accept incoming connections on a connection-based socket. This removes
@@ -1451,7 +1494,7 @@ g_socket_speaks_ipv4 (GSocket *socket)
  * or return %G_IO_ERROR_WOULD_BLOCK if non-blocking I/O is enabled.
  * To be notified of an incoming connection, wait for the %G_IO_IN condition.
  *
- * Returns: a new #GSocket, or %NULL on error.
+ * Returns: (transfer full): a new #GSocket, or %NULL on error.
  *     Free the returned object with g_object_unref().
  *
  * Since: 2.22
@@ -1549,7 +1592,7 @@ g_socket_accept (GSocket       *socket,
  * g_socket_connect:
  * @socket: a #GSocket.
  * @address: a #GSocketAddress specifying the remote address.
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Connect the socket to the specified remote address.
@@ -1589,6 +1632,10 @@ g_socket_connect (GSocket         *socket,
   if (!g_socket_address_to_native (address, &buffer, sizeof buffer, error))
     return FALSE;
 
+  if (socket->priv->remote_address)
+    g_object_unref (socket->priv->remote_address);
+  socket->priv->remote_address = g_object_ref (address);
+
   while (1)
     {
       if (connect (socket->priv->fd, (struct sockaddr *) &buffer,
@@ -1615,8 +1662,11 @@ g_socket_connect (GSocket         *socket,
                  g_prefix_error (error, _("Error connecting: "));
                }
              else
-                g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING,
-                                     _("Connection in progress"));
+                {
+                  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                                       _("Connection in progress"));
+                  socket->priv->connect_pending = TRUE;
+                }
            }
          else
            g_set_error (error, G_IO_ERROR,
@@ -1672,6 +1722,11 @@ g_socket_check_connect_result (GSocket  *socket,
     {
       g_set_error_literal (error, G_IO_ERROR, socket_io_error_from_errno (value),
                            socket_strerror (value));
+      if (socket->priv->remote_address)
+        {
+          g_object_unref (socket->priv->remote_address);
+          socket->priv->remote_address = NULL;
+        }
       return FALSE;
     }
   return TRUE;
@@ -1683,7 +1738,7 @@ g_socket_check_connect_result (GSocket  *socket,
  * @buffer: a buffer to read data into (which should be at least @size
  *     bytes long).
  * @size: the number of bytes you want to read from the socket
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Receive data (up to @size bytes) from a socket. This is mainly used by
@@ -1720,6 +1775,37 @@ g_socket_receive (GSocket       *socket,
                  GCancellable  *cancellable,
                  GError       **error)
 {
+  return g_socket_receive_with_blocking (socket, buffer, size,
+                                        socket->priv->blocking,
+                                        cancellable, error);
+}
+
+/**
+ * g_socket_receive_with_blocking:
+ * @socket: a #GSocket
+ * @buffer: a buffer to read data into (which should be at least @size
+ *     bytes long).
+ * @size: the number of bytes you want to read from the socket
+ * @blocking: whether to do blocking or non-blocking I/O
+ * @cancellable: (allow-none): a %GCancellable or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * This behaves exactly the same as g_socket_receive(), except that
+ * the choice of blocking or non-blocking behavior is determined by
+ * the @blocking argument rather than by @socket's properties.
+ *
+ * Returns: Number of bytes read, or -1 on error
+ *
+ * Since: 2.26
+ */
+gssize
+g_socket_receive_with_blocking (GSocket       *socket,
+                               gchar         *buffer,
+                               gsize          size,
+                               gboolean       blocking,
+                               GCancellable  *cancellable,
+                               GError       **error)
+{
   gssize ret;
 
   g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE);
@@ -1732,7 +1818,7 @@ g_socket_receive (GSocket       *socket,
 
   while (1)
     {
-      if (socket->priv->blocking &&
+      if (blocking &&
          !g_socket_condition_wait (socket,
                                    G_IO_IN, cancellable, error))
        return -1;
@@ -1744,7 +1830,7 @@ g_socket_receive (GSocket       *socket,
          if (errsv == EINTR)
            continue;
 
-         if (socket->priv->blocking)
+         if (blocking)
            {
 #ifdef WSAEWOULDBLOCK
              if (errsv == WSAEWOULDBLOCK)
@@ -1779,7 +1865,7 @@ g_socket_receive (GSocket       *socket,
  * @buffer: a buffer to read data into (which should be at least @size
  *     bytes long).
  * @size: the number of bytes you want to read from the socket
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Receive data (up to @size bytes) from a socket.
@@ -1828,9 +1914,9 @@ g_socket_receive_from (GSocket         *socket,
 /**
  * g_socket_send:
  * @socket: a #GSocket
- * @buffer: the buffer containing the data to send.
+ * @buffer: (array length=size): the buffer containing the data to send.
  * @size: the number of bytes to send
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Tries to send @size bytes from @buffer on the socket. This is
@@ -1860,6 +1946,37 @@ g_socket_send (GSocket       *socket,
               GCancellable  *cancellable,
               GError       **error)
 {
+  return g_socket_send_with_blocking (socket, buffer, size,
+                                     socket->priv->blocking,
+                                     cancellable, error);
+}
+
+/**
+ * g_socket_send_with_blocking:
+ * @socket: a #GSocket
+ * @buffer: (array length=size): the buffer containing the data to send.
+ * @size: the number of bytes to send
+ * @blocking: whether to do blocking or non-blocking I/O
+ * @cancellable: (allow-none): a %GCancellable or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * This behaves exactly the same as g_socket_send(), except that
+ * the choice of blocking or non-blocking behavior is determined by
+ * the @blocking argument rather than by @socket's properties.
+ *
+ * Returns: Number of bytes written (which may be less than @size), or -1
+ * on error
+ *
+ * Since: 2.26
+ */
+gssize
+g_socket_send_with_blocking (GSocket       *socket,
+                            const gchar   *buffer,
+                            gsize          size,
+                            gboolean       blocking,
+                            GCancellable  *cancellable,
+                            GError       **error)
+{
   gssize ret;
 
   g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE);
@@ -1872,7 +1989,7 @@ g_socket_send (GSocket       *socket,
 
   while (1)
     {
-      if (socket->priv->blocking &&
+      if (blocking &&
          !g_socket_condition_wait (socket,
                                    G_IO_OUT, cancellable, error))
        return -1;
@@ -1889,7 +2006,7 @@ g_socket_send (GSocket       *socket,
            win32_unset_event_mask (socket, FD_WRITE);
 #endif
 
-         if (socket->priv->blocking)
+         if (blocking)
            {
 #ifdef WSAEWOULDBLOCK
              if (errsv == WSAEWOULDBLOCK)
@@ -1916,9 +2033,9 @@ g_socket_send (GSocket       *socket,
  * g_socket_send_to:
  * @socket: a #GSocket
  * @address: a #GSocketAddress, or %NULL
- * @buffer: the buffer containing the data to send.
+ * @buffer: (array length=size): the buffer containing the data to send.
  * @size: the number of bytes to send
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Tries to send @size bytes from @buffer to @address. If @address is
@@ -2104,6 +2221,11 @@ g_socket_close (GSocket  *socket,
 
   socket->priv->connected = FALSE;
   socket->priv->closed = TRUE;
+  if (socket->priv->remote_address)
+    {
+      g_object_unref (socket->priv->remote_address);
+      socket->priv->remote_address = NULL;
+    }
 
   return TRUE;
 }
@@ -2291,7 +2413,7 @@ typedef struct {
   GIOCondition  condition;
   GCancellable *cancellable;
   GPollFD       cancel_pollfd;
-  GTimeVal      timeout_time;
+  gint64        timeout_time;
 } GSocketSource;
 
 static gboolean
@@ -2303,19 +2425,20 @@ socket_source_prepare (GSource *source,
   if (g_cancellable_is_cancelled (socket_source->cancellable))
     return TRUE;
 
-  if (socket_source->timeout_time.tv_sec)
+  if (socket_source->timeout_time)
     {
-      GTimeVal now;
+      gint64 now;
 
-      g_source_get_current_time (source, &now);
-      *timeout = ((socket_source->timeout_time.tv_sec - now.tv_sec) * 1000 +
-                 (socket_source->timeout_time.tv_usec - now.tv_usec) / 1000);
+      now = g_source_get_time (source);
+      /* Round up to ensure that we don't try again too early */
+      *timeout = (socket_source->timeout_time - now + 999) / 1000;
       if (*timeout < 0)
-       {
-         socket_source->socket->priv->timed_out = TRUE;
-         socket_source->pollfd.revents = socket_source->condition & (G_IO_IN | G_IO_OUT);
-         return TRUE;
-       }
+        {
+          socket_source->socket->priv->timed_out = TRUE;
+          socket_source->pollfd.revents = socket_source->condition & (G_IO_IN | G_IO_OUT);
+          *timeout = 0;
+          return TRUE;
+        }
     }
   else
     *timeout = -1;
@@ -2346,6 +2469,10 @@ socket_source_dispatch (GSource     *source,
   GSocketSourceFunc func = (GSocketSourceFunc)callback;
   GSocketSource *socket_source = (GSocketSource *)source;
 
+#ifdef G_OS_WIN32
+  socket_source->pollfd.revents = update_condition (socket_source->socket);
+#endif
+
   return (*func) (socket_source->socket,
                  socket_source->pollfd.revents & socket_source->condition,
                  user_data);
@@ -2372,12 +2499,42 @@ socket_source_finalize (GSource *source)
     }
 }
 
+static gboolean
+socket_source_closure_callback (GSocket      *socket,
+                               GIOCondition  condition,
+                               gpointer      data)
+{
+  GClosure *closure = data;
+
+  GValue params[2] = { { 0, }, { 0, } };
+  GValue result_value = { 0, };
+  gboolean result;
+
+  g_value_init (&result_value, G_TYPE_BOOLEAN);
+
+  g_value_init (&params[0], G_TYPE_SOCKET);
+  g_value_set_object (&params[0], socket);
+  g_value_init (&params[1], G_TYPE_IO_CONDITION);
+  g_value_set_flags (&params[1], condition);
+
+  g_closure_invoke (closure, &result_value, 2, params, NULL);
+
+  result = g_value_get_boolean (&result_value);
+  g_value_unset (&result_value);
+  g_value_unset (&params[0]);
+  g_value_unset (&params[1]);
+
+  return result;
+}
+
 static GSourceFuncs socket_source_funcs =
 {
   socket_source_prepare,
   socket_source_check,
   socket_source_dispatch,
-  socket_source_finalize
+  socket_source_finalize,
+  (GSourceFunc)socket_source_closure_callback,
+  (GSourceDummyMarshal)_gio_marshal_BOOLEAN__FLAGS,
 };
 
 static GSource *
@@ -2401,6 +2558,7 @@ socket_source_new (GSocket      *socket,
   condition |= G_IO_HUP | G_IO_ERR;
 
   source = g_source_new (&socket_source_funcs, sizeof (GSocketSource));
+  g_source_set_name (source, "GSocket");
   socket_source = (GSocketSource *)source;
 
   socket_source->socket = g_object_ref (socket);
@@ -2425,24 +2583,20 @@ socket_source_new (GSocket      *socket,
   g_source_add_poll (source, &socket_source->pollfd);
 
   if (socket->priv->timeout)
-    {
-      g_get_current_time (&socket_source->timeout_time);
-      socket_source->timeout_time.tv_sec += socket->priv->timeout;
-    }
+    socket_source->timeout_time = g_get_monotonic_time () +
+                                  socket->priv->timeout * 1000000;
+
   else
-    {
-      socket_source->timeout_time.tv_sec = 0;
-      socket_source->timeout_time.tv_usec = 0;
-    }
+    socket_source->timeout_time = 0;
 
   return source;
 }
 
 /**
- * g_socket_create_source:
+ * g_socket_create_source: (skip)
  * @socket: a #GSocket
  * @condition: a #GIOCondition mask to monitor
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  *
  * Creates a %GSource that can be attached to a %GMainContext to monitor
  * for the availibility of the specified @condition on the socket.
@@ -2464,7 +2618,7 @@ socket_source_new (GSocket      *socket,
  * marked as having had a timeout, and so the next #GSocket I/O method
  * you call will then fail with a %G_IO_ERROR_TIMED_OUT.
  *
- * Returns: a newly allocated %GSource, free with g_source_unref().
+ * Returns: (transfer full): a newly allocated %GSource, free with g_source_unref().
  *
  * Since: 2.22
  */
@@ -2543,7 +2697,7 @@ g_socket_condition_check (GSocket      *socket,
  * g_socket_condition_wait:
  * @socket: a #GSocket
  * @condition: a #GIOCondition mask to wait for
- * @cancellable: a #GCancellable, or %NULL
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
  * @error: a #GError pointer, or %NULL
  *
  * Waits for @condition to become true on @socket. When the condition
@@ -2670,13 +2824,13 @@ g_socket_condition_wait (GSocket       *socket,
  * g_socket_send_message:
  * @socket: a #GSocket
  * @address: a #GSocketAddress, or %NULL
- * @vectors: an array of #GOutputVector structs
+ * @vectors: (array length=num_vectors): an array of #GOutputVector structs
  * @num_vectors: the number of elements in @vectors, or -1
- * @messages: a pointer to an array of #GSocketControlMessages, or
- *   %NULL.
+ * @messages: (array length=num_messages) (allow-none): a pointer to an
+ *   array of #GSocketControlMessages, or %NULL.
  * @num_messages: number of elements in @messages, or -1.
  * @flags: an int containing #GSocketMsgFlags flags
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Send data to @address on @socket.  This is the most complicated and
@@ -2773,6 +2927,8 @@ g_socket_send_message (GSocket                *socket,
     struct msghdr msg;
     gssize result;
 
+   msg.msg_flags = 0;
+
     /* name */
     if (address)
       {
@@ -2826,7 +2982,13 @@ g_socket_send_message (GSocket                *socket,
       for (i = 0; i < num_messages; i++)
        msg.msg_controllen += CMSG_SPACE (g_socket_control_message_get_size (messages[i]));
 
-      msg.msg_control = g_alloca (msg.msg_controllen);
+      if (msg.msg_controllen == 0)
+        msg.msg_control = NULL;
+      else
+        {
+          msg.msg_control = g_alloca (msg.msg_controllen);
+          memset (msg.msg_control, '\0', msg.msg_controllen);
+        }
 
       cmsg = CMSG_FIRSTHDR (&msg);
       for (i = 0; i < num_messages; i++)
@@ -2960,14 +3122,14 @@ g_socket_send_message (GSocket                *socket,
  * g_socket_receive_message:
  * @socket: a #GSocket
  * @address: a pointer to a #GSocketAddress pointer, or %NULL
- * @vectors: an array of #GInputVector structs
+ * @vectors: (array length=num_vectors): an array of #GInputVector structs
  * @num_vectors: the number of elements in @vectors, or -1
- * @messages: a pointer which may be filled with an array of
- *     #GSocketControlMessages, or %NULL
+ * @messages: (array length=num_messages) (allow-none): a pointer which
+ *    may be filled with an array of #GSocketControlMessages, or %NULL
  * @num_messages: a pointer which will be filled with the number of
  *    elements in @messages, or %NULL
  * @flags: a pointer to an int containing #GSocketMsgFlags flags
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: a #GError pointer, or %NULL
  *
  * Receive data from a socket.  This is the most complicated and
@@ -3167,12 +3329,7 @@ g_socket_receive_message (GSocket                 *socket,
     /* decode control messages */
     {
       GPtrArray *my_messages = NULL;
-      const gchar *scm_pointer;
       struct cmsghdr *cmsg;
-      gsize scm_size;
-
-      scm_pointer = (const gchar *) msg.msg_control;
-      scm_size = msg.msg_controllen;
 
       for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg))
        {
@@ -3318,3 +3475,73 @@ g_socket_receive_message (GSocket                 *socket,
   }
 #endif
 }
+
+/**
+ * g_socket_get_credentials:
+ * @socket: a #GSocket.
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Returns the credentials of the foreign process connected to this
+ * socket, if any (e.g. it is only supported for %G_SOCKET_FAMILY_UNIX
+ * sockets).
+ *
+ * If this operation isn't supported on the OS, the method fails with
+ * the %G_IO_ERROR_NOT_SUPPORTED error. On Linux this is implemented
+ * by reading the %SO_PEERCRED option on the underlying socket.
+ *
+ * Other ways to obtain credentials from a foreign peer includes the
+ * #GUnixCredentialsMessage type and
+ * g_unix_connection_send_credentials() /
+ * g_unix_connection_receive_credentials() functions.
+ *
+ * Returns: (transfer full): %NULL if @error is set, otherwise a #GCredentials object
+ * that must be freed with g_object_unref().
+ *
+ * Since: 2.26
+ */
+GCredentials *
+g_socket_get_credentials (GSocket   *socket,
+                          GError   **error)
+{
+  GCredentials *ret;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  ret = NULL;
+
+#ifdef __linux__
+  {
+    struct ucred native_creds;
+    socklen_t optlen;
+    optlen = sizeof (struct ucred);
+    if (getsockopt (socket->priv->fd,
+                    SOL_SOCKET,
+                    SO_PEERCRED,
+                    (void *)&native_creds,
+                    &optlen) != 0)
+      {
+        int errsv = get_socket_errno ();
+        g_set_error (error,
+                     G_IO_ERROR,
+                     socket_io_error_from_errno (errsv),
+                     _("Unable to get pending error: %s"),
+                     socket_strerror (errsv));
+      }
+    else
+      {
+        ret = g_credentials_new ();
+        g_credentials_set_native (ret,
+                                  G_CREDENTIALS_TYPE_LINUX_UCRED,
+                                  &native_creds);
+      }
+  }
+#else
+  g_set_error_literal (error,
+                       G_IO_ERROR,
+                       G_IO_ERROR_NOT_SUPPORTED,
+                       _("g_socket_get_credentials not implemented for this OS"));
+#endif
+
+  return ret;
+}