/* returns false on out-of-memory */
static dbus_bool_t
do_reading (DBusTransport *transport)
{
  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
  DBusString *buffer;
  int bytes_read;
  int total;
  dbus_bool_t oom;
  int saved_errno;

  _dbus_verbose ("fd = %" DBUS_SOCKET_FORMAT "\n",
                 _dbus_socket_printable (socket_transport->fd));
  
  /* No messages without authentication! */
  if (!_dbus_transport_try_to_authenticate (transport))
    return TRUE;

  oom = FALSE;
  
  total = 0;

 again:
  
  /* See if we've exceeded max messages and need to disable reading */
  check_read_watch (transport);
  
  if (total > socket_transport->max_bytes_read_per_iteration)
    {
      _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n",
                     total, socket_transport->max_bytes_read_per_iteration);
      goto out;
    }

  _dbus_assert (socket_transport->read_watch != NULL ||
                transport->disconnected);
  
  if (transport->disconnected)
    goto out;

  if (!dbus_watch_get_enabled (socket_transport->read_watch))
    return TRUE;
  
  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
        bytes_read = _dbus_read_socket (socket_transport->fd,
                                        &socket_transport->encoded_incoming,
                                        socket_transport->max_bytes_read_per_iteration);

      saved_errno = _dbus_save_socket_errno ();

      _dbus_assert (_dbus_string_get_length (&socket_transport->encoded_incoming) ==
                    bytes_read);
      
      if (bytes_read > 0)
        {
          _dbus_message_loader_get_buffer (transport->loader,
                                           &buffer);

          if (!_dbus_auth_decode_data (transport->auth,
                                       &socket_transport->encoded_incoming,
                                       buffer))
            {
              _dbus_verbose ("Out of memory decoding incoming data\n");
              _dbus_message_loader_return_buffer (transport->loader,
                                              buffer);

              oom = TRUE;
              goto out;
            }

          _dbus_message_loader_return_buffer (transport->loader,
                                              buffer);

          _dbus_string_set_length (&socket_transport->encoded_incoming, 0);
          _dbus_string_compact (&socket_transport->encoded_incoming, 2048);
        }
    }
  else
    {
      _dbus_message_loader_get_buffer (transport->loader,
                                       &buffer);

#ifdef HAVE_UNIX_FD_PASSING
      if (DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport))
        {
          int *fds;
          unsigned int 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);
              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);
          saved_errno = _dbus_save_socket_errno ();

          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);
          saved_errno = _dbus_save_socket_errno ();
        }

      _dbus_message_loader_return_buffer (transport->loader,
                                          buffer);
    }

  if (bytes_read < 0)
    {
      /* EINTR already handled for us */

      if (_dbus_get_is_errno_enomem (saved_errno))
        {
          _dbus_verbose ("Out of memory in read()/do_reading()\n");
          oom = TRUE;
          goto out;
        }
      else if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno))
        goto out;
      else
        {
          _dbus_verbose ("Error reading from remote app: %s\n",
                         _dbus_strerror (saved_errno));
          do_io_error (transport);
          goto out;
        }
    }
  else if (bytes_read == 0)
    {
      _dbus_verbose ("Disconnected from remote app\n");
      do_io_error (transport);
      goto out;
    }
  else
    {
      _dbus_verbose (" read %d bytes\n", bytes_read);
      
      total += bytes_read;      

      if (!_dbus_transport_queue_messages (transport))
        {
          oom = TRUE;
          _dbus_verbose (" out of memory when queueing messages we just read in the transport\n");
          goto out;
        }
      
      /* Try reading more data until we get EAGAIN and return, or
       * exceed max bytes per iteration.  If in blocking mode of
       * course we'll block instead of returning.
       */
      goto again;
    }

 out:
  if (oom)
    return FALSE;
  else
    return TRUE;
}
/* returns false on oom */
static dbus_bool_t
do_writing (DBusTransport *transport)
{
  int total;
  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
  dbus_bool_t oom;
  
  /* No messages without authentication! */
  if (!_dbus_transport_try_to_authenticate (transport))
    {
      _dbus_verbose ("Not authenticated, not writing anything\n");
      return TRUE;
    }

  if (transport->disconnected)
    {
      _dbus_verbose ("Not connected, not writing anything\n");
      return TRUE;
    }

#if 1
  _dbus_verbose ("do_writing(), have_messages = %d, fd = %d\n",
                 _dbus_connection_has_messages_to_send_unlocked (transport->connection),
                 socket_transport->fd);
#endif
  
  oom = FALSE;
  total = 0;

  while (!transport->disconnected &&
         _dbus_connection_has_messages_to_send_unlocked (transport->connection))
    {
      int bytes_written;
      DBusMessage *message;
      const DBusString *header;
      const DBusString *body;
      int header_len, body_len;
      int total_bytes_to_write;
      
      if (total > socket_transport->max_bytes_written_per_iteration)
        {
          _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n",
                         total, socket_transport->max_bytes_written_per_iteration);
          goto out;
        }
      
      message = _dbus_connection_get_message_to_send (transport->connection);
      _dbus_assert (message != NULL);
      dbus_message_lock (message);

#if 0
      _dbus_verbose ("writing message %p\n", message);
#endif
      
      _dbus_message_get_network_data (message,
                                      &header, &body);

      header_len = _dbus_string_get_length (header);
      body_len = _dbus_string_get_length (body);

      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,
                                           header, &socket_transport->encoded_outgoing))
                {
                  oom = TRUE;
                  goto out;
                }
              
              if (!_dbus_auth_encode_data (transport->auth,
                                           body, &socket_transport->encoded_outgoing))
                {
                  _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
                  oom = TRUE;
                  goto out;
                }
            }
          
          total_bytes_to_write = _dbus_string_get_length (&socket_transport->encoded_outgoing);

#if 0
          _dbus_verbose ("encoded message is %d bytes\n",
                         total_bytes_to_write);
#endif
          
          bytes_written =
            _dbus_write_socket (socket_transport->fd,
                                &socket_transport->encoded_outgoing,
                                socket_transport->message_bytes_written,
                                total_bytes_to_write - socket_transport->message_bytes_written);
        }
      else
        {
          total_bytes_to_write = header_len + body_len;

#if 0
          _dbus_verbose ("message is %d bytes\n",
                         total_bytes_to_write);
#endif

#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_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
            {
              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));
                }
            }
        }

      if (bytes_written < 0)
        {
          /* EINTR already handled for us */
          
          /* 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
            {
              _dbus_verbose ("Error writing to remote app: %s\n",
                             _dbus_strerror_from_errno ());
              do_io_error (transport);
              goto out;
            }
        }
      else
        {
          _dbus_verbose (" wrote %d bytes of %d\n", bytes_written,
                         total_bytes_to_write);
          
          total += bytes_written;
          socket_transport->message_bytes_written += bytes_written;

          _dbus_assert (socket_transport->message_bytes_written <=
                        total_bytes_to_write);
          
          if (socket_transport->message_bytes_written == total_bytes_to_write)
            {
              socket_transport->message_bytes_written = 0;
              _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
              _dbus_string_compact (&socket_transport->encoded_outgoing, 2048);

              _dbus_connection_message_sent_unlocked (transport->connection,
                                                      message);
            }
        }
    }

 out:
  if (oom)
    return FALSE;
  else
    return TRUE;
}
/* returns false on oom */
static dbus_bool_t
do_writing (DBusTransport *transport)
{
  int total;
  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
  dbus_bool_t oom;
  
  /* No messages without authentication! */
  if (!_dbus_transport_try_to_authenticate (transport))
    {
      _dbus_verbose ("Not authenticated, not writing anything\n");
      return TRUE;
    }

  if (transport->disconnected)
    {
      _dbus_verbose ("Not connected, not writing anything\n");
      return TRUE;
    }

#if 1
  _dbus_verbose ("do_writing(), have_messages = %d, fd = %" DBUS_SOCKET_FORMAT "\n",
                 _dbus_connection_has_messages_to_send_unlocked (transport->connection),
                 _dbus_socket_printable (socket_transport->fd));
#endif
  
  oom = FALSE;
  total = 0;

  while (!transport->disconnected &&
         _dbus_connection_has_messages_to_send_unlocked (transport->connection))
    {
      int bytes_written;
      DBusMessage *message;
      const DBusString *header;
      const DBusString *body;
      int header_len, body_len;
      int total_bytes_to_write;
      int saved_errno;
      
      if (total > socket_transport->max_bytes_written_per_iteration)
        {
          _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n",
                         total, socket_transport->max_bytes_written_per_iteration);
          goto out;
        }
      
      message = _dbus_connection_get_message_to_send (transport->connection);
      _dbus_assert (message != NULL);
      dbus_message_lock (message);

#if 0
      _dbus_verbose ("writing message %p\n", message);
#endif
      
      _dbus_message_get_network_data (message,
                                      &header, &body);

      header_len = _dbus_string_get_length (header);
      body_len = _dbus_string_get_length (body);

      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,
                                           header, &socket_transport->encoded_outgoing))
                {
                  oom = TRUE;
                  goto out;
                }
              
              if (!_dbus_auth_encode_data (transport->auth,
                                           body, &socket_transport->encoded_outgoing))
                {
                  _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
                  oom = TRUE;
                  goto out;
                }
            }
          
          total_bytes_to_write = _dbus_string_get_length (&socket_transport->encoded_outgoing);

#if 0
          _dbus_verbose ("encoded message is %d bytes\n",
                         total_bytes_to_write);
#endif
          
          bytes_written =
            _dbus_write_socket (socket_transport->fd,
                                &socket_transport->encoded_outgoing,
                                socket_transport->message_bytes_written,
                                total_bytes_to_write - socket_transport->message_bytes_written);
          saved_errno = _dbus_save_socket_errno ();
        }
      else
        {
          total_bytes_to_write = header_len + body_len;

#if 0
          _dbus_verbose ("message is %d bytes\n",
                         total_bytes_to_write);
#endif

#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_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);
              saved_errno = _dbus_save_socket_errno ();

              if (bytes_written > 0 && n > 0)
                _dbus_verbose("Wrote %i unix fds\n", n);
            }
          else
#endif
            {
              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));
                }

              saved_errno = _dbus_save_socket_errno ();
            }
        }

      if (bytes_written < 0)
        {
          /* EINTR already handled for us */
          
          /* If the other end closed the socket with close() or shutdown(), we
           * receive EPIPE here but we must not close the socket yet: there
           * might still be some data to read. See:
           * http://lists.freedesktop.org/archives/dbus/2008-March/009526.html
           */
          
          if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno) || _dbus_get_is_errno_epipe (saved_errno))
            goto out;

          /* Since Linux commit 25888e (from 2.6.37-rc4, Nov 2010), sendmsg()
           * on Unix sockets returns -1 errno=ETOOMANYREFS when the passfd
           * mechanism (SCM_RIGHTS) is used recursively with a recursion level
           * of maximum 4. The kernel does not have an API to check whether
           * the passed fds can be forwarded and it can change asynchronously.
           * See:
           * https://bugs.freedesktop.org/show_bug.cgi?id=80163
           */

          else if (_dbus_get_is_errno_etoomanyrefs (saved_errno))
            {
              /* We only send fds in the first byte of the message.
               * ETOOMANYREFS cannot happen after.
               */
              _dbus_assert (socket_transport->message_bytes_written == 0);

              _dbus_verbose (" discard message of %d bytes due to ETOOMANYREFS\n",
                             total_bytes_to_write);

              socket_transport->message_bytes_written = 0;
              _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
              _dbus_string_compact (&socket_transport->encoded_outgoing, 2048);

              /* The message was not actually sent but it needs to be removed
               * from the outgoing queue
               */
              _dbus_connection_message_sent_unlocked (transport->connection,
                                                      message);
            }
          else
            {
              _dbus_verbose ("Error writing to remote app: %s\n",
                             _dbus_strerror (saved_errno));
              do_io_error (transport);
              goto out;
            }
        }
      else
        {
          _dbus_verbose (" wrote %d bytes of %d\n", bytes_written,
                         total_bytes_to_write);
          
          total += bytes_written;
          socket_transport->message_bytes_written += bytes_written;

          _dbus_assert (socket_transport->message_bytes_written <=
                        total_bytes_to_write);
          
          if (socket_transport->message_bytes_written == total_bytes_to_write)
            {
              socket_transport->message_bytes_written = 0;
              _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
              _dbus_string_compact (&socket_transport->encoded_outgoing, 2048);

              _dbus_connection_message_sent_unlocked (transport->connection,
                                                      message);
            }
        }
    }

 out:
  if (oom)
    return FALSE;
  else
    return TRUE;
}
Example #4
0
/**
 * Returns TRUE if the transport supports sending unix fds.
 *
 * @param transport the transport
 * @returns #TRUE if TRUE it is possible to send unix fds across the transport.
 */
dbus_bool_t
_dbus_transport_can_pass_unix_fd(DBusTransport *transport)
{
  return DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport);
}