2004-07-24 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-transport-unix.c
index 82b28e0..3447ae1 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2002, 2003  Red Hat Inc.
  *
- * Licensed under the Academic Free License version 1.2
+ * Licensed under the Academic Free License version 2.0
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,9 +26,6 @@
 #include "dbus-transport-unix.h"
 #include "dbus-transport-protected.h"
 #include "dbus-watch.h"
-#include <sys/types.h>
-#include <sys/time.h>
-#include <unistd.h>
 
 
 /**
@@ -345,40 +342,40 @@ do_authentication (DBusTransport *transport,
                          transport->receive_credentials_pending);
           goto out;
         }
-      
+
+#define TRANSPORT_SIDE(t) ((t)->is_server ? "server" : "client")
       switch (_dbus_auth_do_work (transport->auth))
         {
         case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
-          _dbus_verbose (" auth state: waiting for input\n");
+          _dbus_verbose (" %s auth state: waiting for input\n",
+                         TRANSPORT_SIDE (transport));
           if (!do_reading || !read_data_into_auth (transport, &oom))
             goto out;
           break;
       
         case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
-          _dbus_verbose (" auth state: waiting for memory\n");
+          _dbus_verbose (" %s auth state: waiting for memory\n",
+                         TRANSPORT_SIDE (transport));
           oom = TRUE;
           goto out;
           break;
       
         case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
-          _dbus_verbose (" auth state: bytes to send\n");
+          _dbus_verbose (" %s auth state: bytes to send\n",
+                         TRANSPORT_SIDE (transport));
           if (!do_writing || !write_data_from_auth (transport))
             goto out;
           break;
       
         case DBUS_AUTH_STATE_NEED_DISCONNECT:
-          _dbus_verbose (" auth state: need to disconnect\n");
+          _dbus_verbose (" %s auth state: need to disconnect\n",
+                         TRANSPORT_SIDE (transport));
           do_io_error (transport);
           break;
       
-        case DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES:
-          _dbus_verbose (" auth state: auth with unused bytes\n");
-          /* We'll recover the unused bytes in dbus-transport.c */
-          goto out;
-          break;
-          
         case DBUS_AUTH_STATE_AUTHENTICATED:
-          _dbus_verbose (" auth state: authenticated\n");
+          _dbus_verbose (" %s auth state: authenticated\n",
+                         TRANSPORT_SIDE (transport));
           break;
         }
     }
@@ -586,11 +583,9 @@ do_reading (DBusTransport *transport)
   total = 0;
 
  again:
-
+  
   /* See if we've exceeded max messages and need to disable reading */
   check_read_watch (transport);
-  if (!dbus_watch_get_enabled (unix_transport->read_watch))
-    return TRUE;
   
   if (total > unix_transport->max_bytes_read_per_iteration)
     {
@@ -599,9 +594,15 @@ do_reading (DBusTransport *transport)
       goto out;
     }
 
+  _dbus_assert (unix_transport->read_watch != NULL ||
+                transport->disconnected);
+  
   if (transport->disconnected)
     goto out;
 
+  if (!dbus_watch_get_enabled (unix_transport->read_watch))
+    return TRUE;
+  
   if (_dbus_auth_needs_decoding (transport->auth))
     {
       if (_dbus_string_get_length (&unix_transport->encoded_incoming) > 0)
@@ -685,7 +686,7 @@ do_reading (DBusTransport *transport)
       
       total += bytes_read;      
 
-      if (_dbus_transport_queue_messages (transport) == DBUS_DISPATCH_NEED_MEMORY)
+      if (!_dbus_transport_queue_messages (transport))
         {
           oom = TRUE;
           _dbus_verbose (" out of memory when queueing messages we just read in the transport\n");
@@ -716,8 +717,16 @@ unix_handle_watch (DBusTransport *transport,
   _dbus_assert (watch == unix_transport->read_watch ||
                 watch == unix_transport->write_watch);
   
-  if (flags & (DBUS_WATCH_HANGUP | DBUS_WATCH_ERROR))
+  /* 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 ((flags & DBUS_WATCH_ERROR) ||
+      ((flags & DBUS_WATCH_HANGUP) && !(flags & DBUS_WATCH_READABLE)))
     {
+      _dbus_verbose ("Hang up or error on watch\n");
       _dbus_transport_disconnect (transport);
       return TRUE;
     }
@@ -725,8 +734,8 @@ unix_handle_watch (DBusTransport *transport,
   if (watch == unix_transport->read_watch &&
       (flags & DBUS_WATCH_READABLE))
     {
-#if 1
-      _dbus_verbose ("handling read watch\n");
+#if 0
+      _dbus_verbose ("handling read watch (%x)\n", flags);
 #endif
       if (!do_authentication (transport, TRUE, FALSE))
         return FALSE;
@@ -786,6 +795,14 @@ static dbus_bool_t
 unix_connection_set (DBusTransport *transport)
 {
   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
+
+  _dbus_watch_set_handler (unix_transport->write_watch,
+                           _dbus_connection_handle_watch,
+                           transport->connection, NULL);
+
+  _dbus_watch_set_handler (unix_transport->read_watch,
+                           _dbus_connection_handle_watch,
+                           transport->connection, NULL);
   
   if (!_dbus_connection_add_watch (transport->connection,
                                    unix_transport->write_watch))
@@ -812,7 +829,6 @@ unix_messages_pending (DBusTransport *transport,
   check_write_watch (transport);
 }
 
-/* FIXME use _dbus_poll(), not select() */
 /**
  * @todo We need to have a way to wake up the select sleep if
  * a new iteration request comes in with a flag (read/write) that
@@ -826,10 +842,9 @@ unix_do_iteration (DBusTransport *transport,
                    int            timeout_milliseconds)
 {
   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
-  fd_set read_set;
-  fd_set write_set;
-  dbus_bool_t do_select;
-  int select_res;
+  DBusPollFD poll_fd;
+  int poll_res;
+  int poll_timeout;
 
   _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p\n",
                  flags & DBUS_ITERATION_DO_READING ? "read" : "",
@@ -838,13 +853,6 @@ unix_do_iteration (DBusTransport *transport,
                  unix_transport->read_watch,
                  unix_transport->write_watch);
   
-  /* "again" has to be up here because on EINTR the fd sets become
-   * undefined
-   */
- again:
-  
-  do_select = FALSE;
-
   /* the passed in DO_READING/DO_WRITING flags indicate whether to
    * read/write messages, but regardless of those we may need to block
    * for reading/writing to do auth.  But if we do reading for auth,
@@ -854,24 +862,18 @@ unix_do_iteration (DBusTransport *transport,
    * want to read/write so don't.
    */
 
-  FD_ZERO (&read_set);
-  FD_ZERO (&write_set);
+  poll_fd.fd = unix_transport->fd;
+  poll_fd.events = 0;
   
   if (_dbus_transport_get_is_authenticated (transport))
     {
       if (unix_transport->read_watch &&
           (flags & DBUS_ITERATION_DO_READING))
-        {
-          FD_SET (unix_transport->fd, &read_set);
-          do_select = TRUE;
-        }
+       poll_fd.events |= _DBUS_POLLIN;
       
       if (unix_transport->write_watch &&
           (flags & DBUS_ITERATION_DO_WRITING))
-        {
-          FD_SET (unix_transport->fd, &write_set);
-          do_select = TRUE;
-        }
+       poll_fd.events |= _DBUS_POLLOUT;
     }
   else
     {
@@ -881,50 +883,19 @@ unix_do_iteration (DBusTransport *transport,
 
       if (transport->receive_credentials_pending ||
           auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT)
-        {
-          FD_SET (unix_transport->fd, &read_set);
-          do_select = TRUE;
-        }
+       poll_fd.events |= _DBUS_POLLIN;
 
       if (transport->send_credentials_pending ||
           auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
-        {
-          FD_SET (unix_transport->fd, &write_set);
-          do_select = TRUE;
-        }
+       poll_fd.events |= _DBUS_POLLOUT;
     } 
 
-  if (do_select)
+  if (poll_fd.events)
     {
-      fd_set err_set;
-      struct timeval timeout;
-      dbus_bool_t use_timeout;
-      
-      FD_ZERO (&err_set);
-      FD_SET (unix_transport->fd, &err_set);
-  
       if (flags & DBUS_ITERATION_BLOCK)
-        {
-          if (timeout_milliseconds >= 0)
-            {
-              timeout.tv_sec = timeout_milliseconds / 1000;
-              timeout.tv_usec = (timeout_milliseconds % 1000) * 1000;
-              
-              /* Always use timeout if one is passed in. */
-              use_timeout = TRUE;
-            }
-          else
-            {
-              use_timeout = FALSE; /* NULL timeout to block forever */
-            }
-        }
+       poll_timeout = timeout_milliseconds;
       else
-        {
-          /* 0 timeout to not block */
-          timeout.tv_sec = 0;
-          timeout.tv_usec = 0;
-          use_timeout = TRUE;
-        }
+       poll_timeout = 0;
 
       /* For blocking selects we drop the connection lock here
        * to avoid blocking out connection access during a potentially
@@ -934,22 +905,23 @@ unix_do_iteration (DBusTransport *transport,
       if (flags & DBUS_ITERATION_BLOCK)
        _dbus_connection_unlock (transport->connection);
       
-      select_res = select (unix_transport->fd + 1,
-                          &read_set, &write_set, &err_set,
-                          use_timeout ? &timeout : NULL);
+    again:
+      poll_res = _dbus_poll (&poll_fd, 1, poll_timeout);
+
+      if (poll_res < 0 && errno == EINTR)
+       goto again;
 
       if (flags & DBUS_ITERATION_BLOCK)
        _dbus_connection_lock (transport->connection);
       
-      
-      if (select_res >= 0)
+      if (poll_res >= 0)
         {
-          if (FD_ISSET (unix_transport->fd, &err_set))
+          if (poll_fd.revents & _DBUS_POLLERR)
             do_io_error (transport);
           else
             {
-              dbus_bool_t need_read = FD_ISSET (unix_transport->fd, &read_set);
-              dbus_bool_t need_write = FD_ISSET (unix_transport->fd, &write_set);
+              dbus_bool_t need_read = (poll_fd.revents & _DBUS_POLLIN) > 0;
+              dbus_bool_t need_write = (poll_fd.revents & _DBUS_POLLOUT) > 0;
 
               _dbus_verbose ("in iteration, need_read=%d need_write=%d\n",
                              need_read, need_write);
@@ -961,11 +933,9 @@ unix_do_iteration (DBusTransport *transport,
                 do_writing (transport);
             }
         }
-      else if (errno == EINTR)
-        goto again;
       else
         {
-          _dbus_verbose ("Error from select(): %s\n",
+          _dbus_verbose ("Error from _dbus_poll(): %s\n",
                          _dbus_strerror (errno));
         }
     }
@@ -978,6 +948,18 @@ unix_live_messages_changed (DBusTransport *transport)
   check_read_watch (transport);
 }
 
+
+static dbus_bool_t
+unix_get_unix_fd (DBusTransport *transport,
+                  int           *fd_p)
+{
+  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
+  
+  *fd_p = unix_transport->fd;
+
+  return TRUE;
+}
+
 static DBusTransportVTable unix_vtable = {
   unix_finalize,
   unix_handle_watch,
@@ -985,7 +967,8 @@ static DBusTransportVTable unix_vtable = {
   unix_connection_set,
   unix_messages_pending,
   unix_do_iteration,
-  unix_live_messages_changed
+  unix_live_messages_changed,
+  unix_get_unix_fd
 };
 
 /**
@@ -1018,13 +1001,15 @@ _dbus_transport_new_for_fd (int               fd,
   
   unix_transport->write_watch = _dbus_watch_new (fd,
                                                  DBUS_WATCH_WRITABLE,
-                                                 FALSE);
+                                                 FALSE,
+                                                 NULL, NULL, NULL);
   if (unix_transport->write_watch == NULL)
     goto failed_2;
   
   unix_transport->read_watch = _dbus_watch_new (fd,
                                                 DBUS_WATCH_READABLE,
-                                                FALSE);
+                                                FALSE,
+                                                NULL, NULL, NULL);
   if (unix_transport->read_watch == NULL)
     goto failed_3;
   
@@ -1059,12 +1044,17 @@ _dbus_transport_new_for_fd (int               fd,
  * Creates a new transport for the given Unix domain socket
  * path. This creates a client-side of a transport.
  *
+ * @todo once we add a way to escape paths in a dbus
+ * address, this function needs to do escaping.
+ *
  * @param path the path to the domain socket.
+ * @param abstract #TRUE to use abstract socket namespace
  * @param error address where an error can be returned.
  * @returns a new transport, or #NULL on failure.
  */
 DBusTransport*
 _dbus_transport_new_for_domain_socket (const char     *path,
+                                       dbus_bool_t     abstract,
                                        DBusError      *error)
 {
   int fd;
@@ -1080,15 +1070,18 @@ _dbus_transport_new_for_domain_socket (const char     *path,
     }
 
   fd = -1;
-  
-  if (!_dbus_string_append (&address, "unix:path=") ||
+
+  if ((abstract &&
+       !_dbus_string_append (&address, "unix:abstract=")) ||
+      (!abstract &&
+       !_dbus_string_append (&address, "unix:path=")) ||
       !_dbus_string_append (&address, path))
     {
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       goto failed_0;
     }
   
-  fd = _dbus_connect_unix_socket (path, error);
+  fd = _dbus_connect_unix_socket (path, abstract, error);
   if (fd < 0)
     {
       _DBUS_ASSERT_ERROR_IS_SET (error);