Esempio n. 1
0
/* 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;
}
/* 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;
}