Merge "Optional autogen.sh flag --enable-kdbus-transport added allowing to compile...
[platform/upstream/dbus.git] / dbus / dbus-transport-socket.c
index 6d7c89c..bd19f9b 100644 (file)
  * 
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
+#include <config.h>
 #include "dbus-internals.h"
 #include "dbus-connection-internal.h"
+#include "dbus-nonce.h"
 #include "dbus-transport-socket.h"
 #include "dbus-transport-protected.h"
 #include "dbus-watch.h"
 #include "dbus-credentials.h"
 
-
 /**
  * @defgroup DBusTransportSocket DBusTransport implementations for sockets
  * @ingroup  DBusInternals
@@ -72,7 +73,7 @@ free_watches (DBusTransport *transport)
 {
   DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
 
-  _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+  _dbus_verbose ("start\n");
   
   if (socket_transport->read_watch)
     {
@@ -94,7 +95,7 @@ free_watches (DBusTransport *transport)
       socket_transport->write_watch = NULL;
     }
 
-  _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+  _dbus_verbose ("end\n");
 }
 
 static void
@@ -102,7 +103,7 @@ socket_finalize (DBusTransport *transport)
 {
   DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
 
-  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+  _dbus_verbose ("\n");
   
   free_watches (transport);
 
@@ -134,7 +135,7 @@ check_write_watch (DBusTransport *transport)
   
   _dbus_transport_ref (transport);
 
-  if (_dbus_transport_get_is_authenticated (transport))
+  if (_dbus_transport_try_to_authenticate (transport))
     needed = _dbus_connection_has_messages_to_send_unlocked (transport->connection);
   else
     {
@@ -176,8 +177,7 @@ check_read_watch (DBusTransport *transport)
   DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
   dbus_bool_t need_read_watch;
 
-  _dbus_verbose ("%s: fd = %d\n",
-                 _DBUS_FUNCTION_NAME, socket_transport->fd);
+  _dbus_verbose ("fd = %d\n",socket_transport->fd);
   
   if (transport->connection == NULL)
     return;
@@ -190,9 +190,10 @@ check_read_watch (DBusTransport *transport)
   
   _dbus_transport_ref (transport);
 
-  if (_dbus_transport_get_is_authenticated (transport))
+  if (_dbus_transport_try_to_authenticate (transport))
     need_read_watch =
-      _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size;
+      (_dbus_counter_get_size_value (transport->live_messages) < transport->max_live_messages_size) &&
+      (_dbus_counter_get_unix_fd_value (transport->live_messages) < transport->max_live_messages_unix_fds);
   else
     {
       if (transport->receive_credentials_pending)
@@ -403,7 +404,7 @@ do_authentication (DBusTransport *transport,
 
   oom = FALSE;
   
-  orig_auth_state = _dbus_transport_get_is_authenticated (transport);
+  orig_auth_state = _dbus_transport_try_to_authenticate (transport);
 
   /* This is essential to avoid the check_write_watch() at the end,
    * we don't want to add a write watch in do_iteration before
@@ -418,7 +419,7 @@ do_authentication (DBusTransport *transport,
   
   _dbus_transport_ref (transport);
   
-  while (!_dbus_transport_get_is_authenticated (transport) &&
+  while (!_dbus_transport_try_to_authenticate (transport) &&
          _dbus_transport_get_is_connected (transport))
     {      
       if (!exchange_credentials (transport, do_reading, do_writing))
@@ -476,7 +477,7 @@ do_authentication (DBusTransport *transport,
 
  out:
   if (auth_completed)
-    *auth_completed = (orig_auth_state != _dbus_transport_get_is_authenticated (transport));
+    *auth_completed = (orig_auth_state != _dbus_transport_try_to_authenticate (transport));
   
   check_read_watch (transport);
   check_write_watch (transport);
@@ -497,7 +498,7 @@ do_writing (DBusTransport *transport)
   dbus_bool_t oom;
   
   /* No messages without authentication! */
-  if (!_dbus_transport_get_is_authenticated (transport))
+  if (!_dbus_transport_try_to_authenticate (transport))
     {
       _dbus_verbose ("Not authenticated, not writing anything\n");
       return TRUE;
@@ -551,6 +552,9 @@ do_writing (DBusTransport *transport)
 
       if (_dbus_auth_needs_encoding (transport->auth))
         {
+          /* Does fd passing even make sense with encoded data? */
+          _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport));
+
           if (_dbus_string_get_length (&socket_transport->encoded_outgoing) == 0)
             {
               if (!_dbus_auth_encode_data (transport->auth,
@@ -588,27 +592,53 @@ do_writing (DBusTransport *transport)
 
 #if 0
           _dbus_verbose ("message is %d bytes\n",
-                         total_bytes_to_write);          
+                         total_bytes_to_write);
 #endif
-          
-          if (socket_transport->message_bytes_written < header_len)
+
+#ifdef HAVE_UNIX_FD_PASSING
+          if (socket_transport->message_bytes_written <= 0 && DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport))
             {
+              /* Send the fds along with the first byte of the message */
+              const int *unix_fds;
+              unsigned n;
+
+              _dbus_message_get_unix_fds(message, &unix_fds, &n);
+
               bytes_written =
-                _dbus_write_socket_two (socket_transport->fd,
-                                        header,
-                                        socket_transport->message_bytes_written,
-                                        header_len - socket_transport->message_bytes_written,
-                                        body,
-                                        0, body_len);
+                _dbus_write_socket_with_unix_fds_two (socket_transport->fd,
+                                                      header,
+                                                      socket_transport->message_bytes_written,
+                                                      header_len - socket_transport->message_bytes_written,
+                                                      body,
+                                                      0, body_len,
+                                                      unix_fds,
+                                                      n);
+
+              if (bytes_written > 0 && n > 0)
+                _dbus_verbose("Wrote %i unix fds\n", n);
             }
           else
+#endif
             {
-              bytes_written =
-                _dbus_write_socket (socket_transport->fd,
-                                    body,
-                                    (socket_transport->message_bytes_written - header_len),
-                                    body_len -
-                                    (socket_transport->message_bytes_written - header_len));
+              if (socket_transport->message_bytes_written < header_len)
+                {
+                  bytes_written =
+                    _dbus_write_socket_two (socket_transport->fd,
+                                            header,
+                                            socket_transport->message_bytes_written,
+                                            header_len - socket_transport->message_bytes_written,
+                                            body,
+                                            0, body_len);
+                }
+              else
+                {
+                  bytes_written =
+                    _dbus_write_socket (socket_transport->fd,
+                                        body,
+                                        (socket_transport->message_bytes_written - header_len),
+                                        body_len -
+                                        (socket_transport->message_bytes_written - header_len));
+                }
             }
         }
 
@@ -616,7 +646,11 @@ do_writing (DBusTransport *transport)
         {
           /* EINTR already handled for us */
           
-          if (_dbus_get_is_errno_eagain_or_ewouldblock ())
+          /* For some discussion of why we also ignore EPIPE here, see
+           * http://lists.freedesktop.org/archives/dbus/2008-March/009526.html
+           */
+          
+          if (_dbus_get_is_errno_eagain_or_ewouldblock () || _dbus_get_is_errno_epipe ())
             goto out;
           else
             {
@@ -643,8 +677,8 @@ do_writing (DBusTransport *transport)
               _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
               _dbus_string_compact (&socket_transport->encoded_outgoing, 2048);
 
-              _dbus_connection_message_sent (transport->connection,
-                                             message);
+              _dbus_connection_message_sent_unlocked (transport->connection,
+                                                      message);
             }
         }
     }
@@ -666,11 +700,10 @@ do_reading (DBusTransport *transport)
   int total;
   dbus_bool_t oom;
 
-  _dbus_verbose ("%s: fd = %d\n", _DBUS_FUNCTION_NAME,
-                 socket_transport->fd);
+  _dbus_verbose ("fd = %d\n",socket_transport->fd);
   
   /* No messages without authentication! */
-  if (!_dbus_transport_get_is_authenticated (transport))
+  if (!_dbus_transport_try_to_authenticate (transport))
     return TRUE;
 
   oom = FALSE;
@@ -700,6 +733,9 @@ do_reading (DBusTransport *transport)
   
   if (_dbus_auth_needs_decoding (transport->auth))
     {
+      /* Does fd passing even make sense with encoded data? */
+      _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport));
+
       if (_dbus_string_get_length (&socket_transport->encoded_incoming) > 0)
         bytes_read = _dbus_string_get_length (&socket_transport->encoded_incoming);
       else
@@ -744,15 +780,42 @@ do_reading (DBusTransport *transport)
     {
       _dbus_message_loader_get_buffer (transport->loader,
                                        &buffer);
-      
-      bytes_read = _dbus_read_socket (socket_transport->fd,
-                                      buffer, socket_transport->max_bytes_read_per_iteration);
-      
+
+#ifdef HAVE_UNIX_FD_PASSING
+      if (DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport))
+        {
+          int *fds, n_fds;
+
+          if (!_dbus_message_loader_get_unix_fds(transport->loader, &fds, &n_fds))
+            {
+              _dbus_verbose ("Out of memory reading file descriptors\n");
+              _dbus_message_loader_return_buffer (transport->loader, buffer, 0);
+              oom = TRUE;
+              goto out;
+            }
+
+          bytes_read = _dbus_read_socket_with_unix_fds(socket_transport->fd,
+                                                       buffer,
+                                                       socket_transport->max_bytes_read_per_iteration,
+                                                       fds, &n_fds);
+
+          if (bytes_read >= 0 && n_fds > 0)
+            _dbus_verbose("Read %i unix fds\n", n_fds);
+
+          _dbus_message_loader_return_unix_fds(transport->loader, fds, bytes_read < 0 ? 0 : n_fds);
+        }
+      else
+#endif
+        {
+          bytes_read = _dbus_read_socket (socket_transport->fd,
+                                          buffer, socket_transport->max_bytes_read_per_iteration);
+        }
+
       _dbus_message_loader_return_buffer (transport->loader,
                                           buffer,
                                           bytes_read < 0 ? 0 : bytes_read);
     }
-  
+
   if (bytes_read < 0)
     {
       /* EINTR already handled for us */
@@ -807,6 +870,25 @@ do_reading (DBusTransport *transport)
 }
 
 static dbus_bool_t
+unix_error_with_read_to_come (DBusTransport *itransport,
+                              DBusWatch     *watch,
+                              unsigned int   flags)
+{
+  DBusTransportSocket *transport = (DBusTransportSocket *) itransport;
+
+  if (!(flags & DBUS_WATCH_HANGUP || flags & DBUS_WATCH_ERROR))
+    return FALSE;
+   
+  /* If we have a read watch enabled ...
+     we -might have data incoming ... => handle the HANGUP there */
+  if (watch != transport->read_watch &&
+      _dbus_watch_get_enabled (transport->read_watch))
+    return FALSE;
+      
+  return TRUE; 
+}
+
+static dbus_bool_t
 socket_handle_watch (DBusTransport *transport,
                    DBusWatch     *watch,
                    unsigned int   flags)
@@ -817,14 +899,11 @@ socket_handle_watch (DBusTransport *transport,
                 watch == socket_transport->write_watch);
   _dbus_assert (watch != NULL);
   
-  /* Disconnect in case of an error.  In case of hangup do not
-   * disconnect the transport because data can still be in the buffer
-   * and do_reading may need several iteration to read it all (because
-   * of its max_bytes_read_per_iteration limit).  The condition where
-   * flags == HANGUP (without READABLE) probably never happen in fact.
+  /* If we hit an error here on a write watch, don't disconnect the transport yet because data can
+   * still be in the buffer and do_reading may need several iteration to read
+   * it all (because of its max_bytes_read_per_iteration limit). 
    */
-  if ((flags & DBUS_WATCH_ERROR) ||
-      ((flags & DBUS_WATCH_HANGUP) && !(flags & DBUS_WATCH_READABLE)))
+  if (!(flags & DBUS_WATCH_READABLE) && unix_error_with_read_to_come (transport, watch, flags))
     {
       _dbus_verbose ("Hang up or error on watch\n");
       _dbus_transport_disconnect (transport);
@@ -903,7 +982,7 @@ socket_disconnect (DBusTransport *transport)
 {
   DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
 
-  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+  _dbus_verbose ("\n");
   
   free_watches (transport);
   
@@ -976,7 +1055,7 @@ socket_do_iteration (DBusTransport *transport,
   poll_fd.fd = socket_transport->fd;
   poll_fd.events = 0;
   
-  if (_dbus_transport_get_is_authenticated (transport))
+  if (_dbus_transport_try_to_authenticate (transport))
     {
       /* This is kind of a hack; if we have stuff to write, then try
        * to avoid the poll. This is probably about a 5% speedup on an
@@ -1038,7 +1117,7 @@ socket_do_iteration (DBusTransport *transport,
        */
       if (flags & DBUS_ITERATION_BLOCK)
         {
-          _dbus_verbose ("unlock %s pre poll\n", _DBUS_FUNCTION_NAME);
+          _dbus_verbose ("unlock pre poll\n");
           _dbus_connection_unlock (transport->connection);
         }
       
@@ -1050,7 +1129,7 @@ socket_do_iteration (DBusTransport *transport,
 
       if (flags & DBUS_ITERATION_BLOCK)
         {
-          _dbus_verbose ("lock %s post poll\n", _DBUS_FUNCTION_NAME);
+          _dbus_verbose ("lock post poll\n");
           _dbus_connection_lock (transport->connection);
         }
       
@@ -1184,7 +1263,11 @@ _dbus_transport_new_for_socket (int               fd,
                                   &socket_vtable,
                                   server_guid, address))
     goto failed_4;
-  
+
+#ifdef HAVE_UNIX_FD_PASSING
+  _dbus_auth_set_unix_fd_possible(socket_transport->base.auth, _dbus_socket_can_pass_unix_fd(fd));
+#endif
+
   socket_transport->fd = fd;
   socket_transport->message_bytes_written = 0;
   
@@ -1195,8 +1278,10 @@ _dbus_transport_new_for_socket (int               fd,
   return (DBusTransport*) socket_transport;
 
  failed_4:
+  _dbus_watch_invalidate (socket_transport->read_watch);
   _dbus_watch_unref (socket_transport->read_watch);
  failed_3:
+  _dbus_watch_invalidate (socket_transport->write_watch);
   _dbus_watch_unref (socket_transport->write_watch);
  failed_2:
   _dbus_string_free (&socket_transport->encoded_incoming);
@@ -1214,6 +1299,7 @@ _dbus_transport_new_for_socket (int               fd,
  * @param host the host to connect to
  * @param port the port to connect to
  * @param family the address family to connect to
+ * @param noncefile path to nonce file
  * @param error location to store reason for failure.
  * @returns a new transport, or #NULL on failure.
  */
@@ -1221,6 +1307,7 @@ DBusTransport*
 _dbus_transport_new_for_tcp_socket (const char     *host,
                                     const char     *port,
                                     const char     *family,
+                                    const char     *noncefile,
                                     DBusError      *error)
 {
   int fd;
@@ -1238,7 +1325,7 @@ _dbus_transport_new_for_tcp_socket (const char     *host,
   if (host == NULL)
     host = "localhost";
 
-  if (!_dbus_string_append (&address, "tcp:"))
+  if (!_dbus_string_append (&address, noncefile ? "nonce-tcp:" : "tcp:"))
     goto error;
 
   if (!_dbus_string_append (&address, "host=") ||
@@ -1250,11 +1337,16 @@ _dbus_transport_new_for_tcp_socket (const char     *host,
     goto error;
 
   if (family != NULL &&
-      (!_dbus_string_append (&address, "family=") ||
+      (!_dbus_string_append (&address, ",family=") ||
        !_dbus_string_append (&address, family)))
     goto error;
 
-  fd = _dbus_connect_tcp_socket (host, port, family, error);
+  if (noncefile != NULL &&
+      (!_dbus_string_append (&address, ",noncefile=") ||
+       !_dbus_string_append (&address, noncefile)))
+    goto error;
+
+  fd = _dbus_connect_tcp_socket_with_nonce (host, port, family, noncefile, error);
   if (fd < 0)
     {
       _DBUS_ASSERT_ERROR_IS_SET (error);
@@ -1262,22 +1354,18 @@ _dbus_transport_new_for_tcp_socket (const char     *host,
       return NULL;
     }
 
-  _dbus_fd_set_close_on_exec (fd);
-  
   _dbus_verbose ("Successfully connected to tcp socket %s:%s\n",
                  host, port);
   
   transport = _dbus_transport_new_for_socket (fd, NULL, &address);
+  _dbus_string_free (&address);
   if (transport == NULL)
     {
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       _dbus_close_socket (fd, NULL);
-      _dbus_string_free (&address);
       fd = -1;
     }
 
-  _dbus_string_free (&address);
-  
   return transport;
 
 error:
@@ -1300,23 +1388,34 @@ _dbus_transport_open_socket(DBusAddressEntry  *entry,
                             DBusError         *error)
 {
   const char *method;
+  dbus_bool_t isTcp;
+  dbus_bool_t isNonceTcp;
   
   method = dbus_address_entry_get_method (entry);
   _dbus_assert (method != NULL);
 
-  if (strcmp (method, "tcp") == 0)
+  isTcp = strcmp (method, "tcp") == 0;
+  isNonceTcp = strcmp (method, "nonce-tcp") == 0;
+
+  if (isTcp || isNonceTcp)
     {
       const char *host = dbus_address_entry_get_value (entry, "host");
       const char *port = dbus_address_entry_get_value (entry, "port");
       const char *family = dbus_address_entry_get_value (entry, "family");
+      const char *noncefile = dbus_address_entry_get_value (entry, "noncefile");
+
+      if ((isNonceTcp == TRUE) != (noncefile != NULL)) {
+          _dbus_set_bad_address (error, method, "noncefile", NULL);
+          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+      }
 
       if (port == NULL)
         {
-          _dbus_set_bad_address (error, "tcp", "port", NULL);
+          _dbus_set_bad_address (error, method, "port", NULL);
           return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
         }
 
-      *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, error);
+      *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, noncefile, error);
       if (*transport_p == NULL)
         {
           _DBUS_ASSERT_ERROR_IS_SET (error);