GSocket: Use MSG_CMSG_CLOEXEC
[platform/upstream/glib.git] / gio / gsocket.c
index aa0a39c..97120a6 100644 (file)
 
 #include "gsocket.h"
 
+#ifdef G_OS_UNIX
+#include "glib-unix.h"
+#endif
+
 #include <errno.h>
 #include <signal.h>
 #include <string.h>
@@ -240,22 +244,17 @@ static void
 set_fd_nonblocking (int fd)
 {
 #ifndef G_OS_WIN32
-  glong arg;
+  GError *error = NULL;
 #else
   gulong arg;
 #endif
 
 #ifndef G_OS_WIN32
-  if ((arg = fcntl (fd, F_GETFL, NULL)) < 0)
+  if (!g_unix_set_fd_nonblocking (fd, TRUE, &error))
     {
-      g_warning ("Error getting socket status flags: %s", socket_strerror (errno));
-      arg = 0;
+      g_warning ("Error setting socket nonblocking: %s", error->message);
+      g_clear_error (&error);
     }
-
-  arg = arg | O_NONBLOCK;
-
-  if (fcntl (fd, F_SETFL, arg) < 0)
-      g_warning ("Error setting socket status flags: %s", socket_strerror (errno));
 #else
   arg = TRUE;
 
@@ -1756,15 +1755,17 @@ g_socket_check_connect_result (GSocket  *socket,
  * received, the additional data will be returned in future calls to
  * g_socket_receive().
  *
- * If the socket is in blocking mode the call will block until there is
- * some data to receive or there is an error. If there is no data available
- * and the socket is in non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error
- * will be returned. To be notified when data is available, wait for the
+ * If the socket is in blocking mode the call will block until there
+ * is some data to receive, the connection is closed, or there is an
+ * error. If there is no data available and the socket is in
+ * non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error will be
+ * returned. To be notified when data is available, wait for the
  * %G_IO_IN condition.
  *
  * On error -1 is returned and @error is set accordingly.
  *
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
  *
  * Since: 2.22
  */
@@ -1794,7 +1795,8 @@ g_socket_receive (GSocket       *socket,
  * 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
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
  *
  * Since: 2.26
  */
@@ -1876,7 +1878,8 @@ g_socket_receive_with_blocking (GSocket       *socket,
  *
  * See g_socket_receive() for additional information.
  *
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
  *
  * Since: 2.22
  */
@@ -3182,14 +3185,16 @@ g_socket_send_message (GSocket                *socket,
  * sufficiently-large buffer.
  *
  * If the socket is in blocking mode the call will block until there
- * is some data to receive or there is an error. If there is no data
- * available and the socket is in non-blocking mode, a
- * %G_IO_ERROR_WOULD_BLOCK error will be returned. To be notified when
- * data is available, wait for the %G_IO_IN condition.
+ * is some data to receive, the connection is closed, or there is an
+ * error. If there is no data available and the socket is in
+ * non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error will be
+ * returned. To be notified when data is available, wait for the
+ * %G_IO_IN condition.
  *
  * On error -1 is returned and @error is set accordingly.
  *
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
  *
  * Since: 2.22
  */
@@ -3285,6 +3290,14 @@ g_socket_receive_message (GSocket                 *socket,
     else
       msg.msg_flags = 0;
 
+    /* We always set the close-on-exec flag so we don't leak file
+     * descriptors into child processes.  Note that gunixfdmessage.c
+     * will later call fcntl (fd, FD_CLOEXEC), but that isn't atomic.
+     */
+#ifdef MSG_CMSG_CLOEXEC
+    msg.msg_flags |= MSG_CMSG_CLOEXEC;
+#endif
+
     /* do it */
     while (1)
       {
@@ -3294,6 +3307,14 @@ g_socket_receive_message (GSocket                 *socket,
          return -1;
 
        result = recvmsg (socket->priv->fd, &msg, msg.msg_flags);
+#ifdef MSG_CMSG_CLOEXEC        
+       if (result < 0 && get_socket_errno () == EINVAL)
+         {
+           /* We must be running on an old kernel.  Call without the flag. */
+           msg.msg_flags &= ~(MSG_CMSG_CLOEXEC);
+           result = recvmsg (socket->priv->fd, &msg, msg.msg_flags);
+         }
+#endif
 
        if (result < 0)
          {