X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-transport-unix.c;h=6ba5c0b5384a7210c689b78141cd0ad4f69c1a3e;hb=f67f45979b48ce63fb82179a7ec261e5e3ec3395;hp=d2bc6d7ffd0979235f59ca159a55ed411cb17959;hpb=6d54407dbb089dd0b0d8d79189b029c4d82bae81;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-transport-unix.c b/dbus/dbus-transport-unix.c index d2bc6d7..6ba5c0b 100644 --- a/dbus/dbus-transport-unix.c +++ b/dbus/dbus-transport-unix.c @@ -1,9 +1,9 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-transport-unix.c UNIX socket subclasses of DBusTransport * - * Copyright (C) 2002, 2003 Red Hat Inc. + * Copyright (C) 2002, 2003, 2004 Red Hat Inc. * - * Licensed under the Academic Free License version 1.2 + * Licensed under the Academic Free License version 2.1 * * 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 @@ -17,19 +17,22 @@ * * 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 + +#include + #include "dbus-internals.h" #include "dbus-connection-internal.h" #include "dbus-transport-unix.h" +#include "dbus-transport-socket.h" #include "dbus-transport-protected.h" #include "dbus-watch.h" -#include -#include -#include - +#include "dbus-sysdeps-unix.h" +#include "dbus-test.h" /** * @defgroup DBusTransportUnix DBusTransport implementations for UNIX @@ -40,1112 +43,400 @@ */ /** - * Opaque object representing a Unix file descriptor transport. - */ -typedef struct DBusTransportUnix DBusTransportUnix; - -/** - * Implementation details of DBusTransportUnix. All members are private. + * 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. */ -struct DBusTransportUnix +DBusTransport* +_dbus_transport_new_for_domain_socket (const char *path, + dbus_bool_t abstract, + DBusError *error) { - DBusTransport base; /**< Parent instance */ - int fd; /**< File descriptor. */ - DBusWatch *read_watch; /**< Watch for readability. */ - DBusWatch *write_watch; /**< Watch for writability. */ + int fd; + DBusTransport *transport; + DBusString address; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - int max_bytes_read_per_iteration; /**< To avoid blocking too long. */ - int max_bytes_written_per_iteration; /**< To avoid blocking too long. */ + if (!_dbus_string_init (&address)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } - int message_bytes_written; /**< Number of bytes of current - * outgoing message that have - * been written. - */ - DBusString encoded_outgoing; /**< Encoded version of current - * outgoing message. - */ - DBusString encoded_incoming; /**< Encoded version of current - * incoming data. - */ -}; + fd = -1; -static void -free_watches (DBusTransport *transport) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; + 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; + } - if (unix_transport->read_watch) + fd = _dbus_connect_unix_socket (path, abstract, error); + if (fd < 0) { - if (transport->connection) - _dbus_connection_remove_watch (transport->connection, - unix_transport->read_watch); - _dbus_watch_invalidate (unix_transport->read_watch); - _dbus_watch_unref (unix_transport->read_watch); - unix_transport->read_watch = NULL; + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed_0; } - if (unix_transport->write_watch) + _dbus_verbose ("Successfully connected to unix socket %s\n", + path); + + transport = _dbus_transport_new_for_socket (fd, NULL, &address); + if (transport == NULL) { - if (transport->connection) - _dbus_connection_remove_watch (transport->connection, - unix_transport->write_watch); - _dbus_watch_invalidate (unix_transport->write_watch); - _dbus_watch_unref (unix_transport->write_watch); - unix_transport->write_watch = NULL; + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_1; } -} - -static void -unix_finalize (DBusTransport *transport) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - free_watches (transport); - - _dbus_string_free (&unix_transport->encoded_outgoing); - _dbus_string_free (&unix_transport->encoded_incoming); + _dbus_string_free (&address); - _dbus_transport_finalize_base (transport); + return transport; - _dbus_assert (unix_transport->read_watch == NULL); - _dbus_assert (unix_transport->write_watch == NULL); - - dbus_free (transport); + failed_1: + _dbus_close_socket (fd, NULL); + failed_0: + _dbus_string_free (&address); + return NULL; } -static void -check_write_watch (DBusTransport *transport) +/** + * Creates a new transport for the given binary and arguments. This + * creates a client-side of a transport. The process will be forked + * off and executed with stdin/stdout connected to a local AF_UNIX + * socket. + * + * @param path the path to the domain socket. + * @param argv Parameters list + * @param error address where an error can be returned. + * @returns a new transport, or #NULL on failure. + */ +static DBusTransport* +_dbus_transport_new_for_exec (const char *path, + char *const argv[], + DBusError *error) { - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - dbus_bool_t need_write_watch; + int fd; + DBusTransport *transport; + DBusString address; + unsigned i; + char *escaped; - if (transport->connection == NULL) - return; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - if (transport->disconnected) + if (!_dbus_string_init (&address)) { - _dbus_assert (unix_transport->write_watch == NULL); - return; + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; } - - _dbus_transport_ref (transport); - if (_dbus_transport_get_is_authenticated (transport)) - need_write_watch = transport->messages_need_sending; - else - need_write_watch = transport->send_credentials_pending || - _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND; - - _dbus_connection_toggle_watch (transport->connection, - unix_transport->write_watch, - need_write_watch); - - _dbus_transport_unref (transport); -} - -static void -check_read_watch (DBusTransport *transport) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - dbus_bool_t need_read_watch; - - if (transport->connection == NULL) - return; + fd = -1; - if (transport->disconnected) + escaped = dbus_address_escape_value (path); + if (!escaped) { - _dbus_assert (unix_transport->read_watch == NULL); - return; + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; } - - _dbus_transport_ref (transport); - if (_dbus_transport_get_is_authenticated (transport)) - need_read_watch = - _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size; - else - need_read_watch = transport->receive_credentials_pending || - _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_WAITING_FOR_INPUT; - - _dbus_connection_toggle_watch (transport->connection, - unix_transport->read_watch, - need_read_watch); + if (!_dbus_string_append (&address, "unixexec:path=") || + !_dbus_string_append (&address, escaped)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + dbus_free (escaped); + goto failed; + } - _dbus_transport_unref (transport); -} + dbus_free (escaped); -static void -do_io_error (DBusTransport *transport) -{ - _dbus_transport_ref (transport); - _dbus_transport_disconnect (transport); - _dbus_transport_unref (transport); -} + if (argv) + { + for (i = 0; argv[i]; i++) + { + dbus_bool_t success; -/* return value is whether we successfully read any new data. */ -static dbus_bool_t -read_data_into_auth (DBusTransport *transport, - dbus_bool_t *oom) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - DBusString *buffer; - int bytes_read; - - *oom = FALSE; + escaped = dbus_address_escape_value (argv[i]); + if (!escaped) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } - _dbus_auth_get_buffer (transport->auth, &buffer); - - bytes_read = _dbus_read (unix_transport->fd, - buffer, unix_transport->max_bytes_read_per_iteration); + success = _dbus_string_append_printf (&address, ",argv%u=%s", i, escaped); + dbus_free (escaped); - _dbus_auth_return_buffer (transport->auth, buffer, - bytes_read > 0 ? bytes_read : 0); + if (!success) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + } + } - if (bytes_read > 0) + fd = _dbus_connect_exec (path, argv, error); + if (fd < 0) { - _dbus_verbose (" read %d bytes in auth phase\n", bytes_read); - - return TRUE; + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; } - else if (bytes_read < 0) - { - /* EINTR already handled for us */ - if (errno == ENOMEM) - { - *oom = TRUE; - } - else if (errno == EAGAIN || - errno == EWOULDBLOCK) - ; /* do nothing, just return FALSE below */ - else - { - _dbus_verbose ("Error reading from remote app: %s\n", - _dbus_strerror (errno)); - do_io_error (transport); - } + _dbus_verbose ("Successfully connected to process %s\n", + path); - return FALSE; - } - else + transport = _dbus_transport_new_for_socket (fd, NULL, &address); + if (transport == NULL) { - _dbus_assert (bytes_read == 0); - - _dbus_verbose ("Disconnected from remote app\n"); - do_io_error (transport); - - return FALSE; + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; } -} -/* Return value is whether we successfully wrote any bytes */ -static dbus_bool_t -write_data_from_auth (DBusTransport *transport) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - int bytes_written; - const DBusString *buffer; + _dbus_string_free (&address); - if (!_dbus_auth_get_bytes_to_send (transport->auth, - &buffer)) - return FALSE; - - bytes_written = _dbus_write (unix_transport->fd, - buffer, - 0, _dbus_string_get_length (buffer)); + return transport; - if (bytes_written > 0) - { - _dbus_auth_bytes_sent (transport->auth, bytes_written); - return TRUE; - } - else if (bytes_written < 0) - { - /* EINTR already handled for us */ - - if (errno == EAGAIN || - errno == EWOULDBLOCK) - ; - else - { - _dbus_verbose ("Error writing to remote app: %s\n", - _dbus_strerror (errno)); - do_io_error (transport); - } - } + failed: + if (fd >= 0) + _dbus_close_socket (fd, NULL); - return FALSE; + _dbus_string_free (&address); + return NULL; } -static void -exchange_credentials (DBusTransport *transport, - dbus_bool_t do_reading, - dbus_bool_t do_writing) +/** + * Opens platform specific transport types. + * + * @param entry the address entry to try opening + * @param transport_p return location for the opened transport + * @param error error to be set + * @returns result of the attempt + */ +DBusTransportOpenResult +_dbus_transport_open_platform_specific (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error) { - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; + const char *method; + + method = dbus_address_entry_get_method (entry); + _dbus_assert (method != NULL); - if (do_writing && transport->send_credentials_pending) + if (strcmp (method, "unix") == 0) { - if (_dbus_send_credentials_unix_socket (unix_transport->fd, - NULL)) - { - transport->send_credentials_pending = FALSE; - } - else + const char *path = dbus_address_entry_get_value (entry, "path"); + const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir"); + const char *abstract = dbus_address_entry_get_value (entry, "abstract"); + + if (tmpdir != NULL) { - _dbus_verbose ("Failed to write credentials\n"); - do_io_error (transport); + _dbus_set_bad_address (error, NULL, NULL, + "cannot use the \"tmpdir\" option for an address to connect to, only in an address to listen on"); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; } - } - - if (do_reading && transport->receive_credentials_pending) - { - if (_dbus_read_credentials_unix_socket (unix_transport->fd, - &transport->credentials, - NULL)) + + if (path == NULL && abstract == NULL) { - transport->receive_credentials_pending = FALSE; + _dbus_set_bad_address (error, "unix", + "path or abstract", + NULL); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; } - else + + if (path != NULL && abstract != NULL) { - _dbus_verbose ("Failed to read credentials\n"); - do_io_error (transport); + _dbus_set_bad_address (error, NULL, NULL, + "can't specify both \"path\" and \"abstract\" options in an address"); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; } - } - if (!(transport->send_credentials_pending || - transport->receive_credentials_pending)) - { - _dbus_auth_set_credentials (transport->auth, - &transport->credentials); - } -} - -static dbus_bool_t -do_authentication (DBusTransport *transport, - dbus_bool_t do_reading, - dbus_bool_t do_writing) -{ - dbus_bool_t oom; - - _dbus_transport_ref (transport); - - oom = FALSE; - - while (!_dbus_transport_get_is_authenticated (transport) && - _dbus_transport_get_is_connected (transport)) - { - exchange_credentials (transport, do_reading, do_writing); - - if (transport->send_credentials_pending || - transport->receive_credentials_pending) + if (path) + *transport_p = _dbus_transport_new_for_domain_socket (path, FALSE, + error); + else + *transport_p = _dbus_transport_new_for_domain_socket (abstract, TRUE, + error); + if (*transport_p == NULL) { - _dbus_verbose ("send_credentials_pending = %d receive_credentials_pending = %d\n", - transport->send_credentials_pending, - transport->receive_credentials_pending); - goto out; + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; } - - switch (_dbus_auth_do_work (transport->auth)) + else { - case DBUS_AUTH_STATE_WAITING_FOR_INPUT: - _dbus_verbose (" auth state: waiting for input\n"); - 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"); - oom = TRUE; - goto out; - break; - - case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND: - _dbus_verbose (" auth state: bytes to send\n"); - 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"); - 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"); - break; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_OK; } } - - out: - check_read_watch (transport); - check_write_watch (transport); - _dbus_transport_unref (transport); - - if (oom) - return FALSE; - else - return TRUE; -} - -/* returns false on oom */ -static dbus_bool_t -do_writing (DBusTransport *transport) -{ - int total; - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - dbus_bool_t oom; - - /* No messages without authentication! */ - if (!_dbus_transport_get_is_authenticated (transport)) - return TRUE; - - if (transport->disconnected) - return TRUE; - - oom = FALSE; - total = 0; - - while (!transport->disconnected && - _dbus_connection_have_messages_to_send (transport->connection)) + else if (strcmp (method, "unixexec") == 0) { - int bytes_written; - DBusMessage *message; - const DBusString *header; - const DBusString *body; - int header_len, body_len; - int total_bytes_to_write; - - if (total > unix_transport->max_bytes_written_per_iteration) + const char *path; + unsigned i; + char **argv; + + path = dbus_address_entry_get_value (entry, "path"); + if (path == NULL) { - _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n", - total, unix_transport->max_bytes_written_per_iteration); - goto out; + _dbus_set_bad_address (error, NULL, NULL, + "No process path specified"); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; } - if (!dbus_watch_get_enabled (unix_transport->write_watch)) + /* First count argv arguments */ + for (i = 1; ; i++) { - _dbus_verbose ("write watch disabled, not writing more stuff\n"); - goto out; - } - - message = _dbus_connection_get_message_to_send (transport->connection); - _dbus_assert (message != NULL); - _dbus_message_lock (message); + char t[4+20+1]; /* "argv" plus space for a formatted base 10 64bit integer, plus NUL */ -#if 0 - _dbus_verbose ("writing message %p\n", message); -#endif - - _dbus_message_get_network_data (message, - &header, &body); + snprintf (t, sizeof(t), "argv%u", i); - header_len = _dbus_string_get_length (header); - body_len = _dbus_string_get_length (body); + if (!dbus_address_entry_get_value (entry, t)) + break; + } - if (_dbus_auth_needs_encoding (transport->auth)) + /* Allocate string array */ + argv = dbus_new0 (char*, i+1); + if (!argv) { - if (_dbus_string_get_length (&unix_transport->encoded_outgoing) == 0) - { - if (!_dbus_auth_encode_data (transport->auth, - header, &unix_transport->encoded_outgoing)) - { - oom = TRUE; - goto out; - } - - if (!_dbus_auth_encode_data (transport->auth, - body, &unix_transport->encoded_outgoing)) - { - _dbus_string_set_length (&unix_transport->encoded_outgoing, 0); - oom = TRUE; - goto out; - } - } - - total_bytes_to_write = _dbus_string_get_length (&unix_transport->encoded_outgoing); - -#if 0 - _dbus_verbose ("encoded message is %d bytes\n", - total_bytes_to_write); -#endif - - bytes_written = - _dbus_write (unix_transport->fd, - &unix_transport->encoded_outgoing, - unix_transport->message_bytes_written, - total_bytes_to_write - unix_transport->message_bytes_written); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; } - else + + /* Fill in string array */ + for (i = 0; ; i++) { - total_bytes_to_write = header_len + body_len; + char t[4+20+1]; + const char *p; -#if 0 - _dbus_verbose ("message is %d bytes\n", - total_bytes_to_write); -#endif - - if (unix_transport->message_bytes_written < header_len) - { - bytes_written = - _dbus_write_two (unix_transport->fd, - header, - unix_transport->message_bytes_written, - header_len - unix_transport->message_bytes_written, - body, - 0, body_len); - } - else - { - bytes_written = - _dbus_write (unix_transport->fd, - body, - (unix_transport->message_bytes_written - header_len), - body_len - - (unix_transport->message_bytes_written - header_len)); - } - } + snprintf (t, sizeof(t), "argv%u", i); - if (bytes_written < 0) - { - /* EINTR already handled for us */ - - if (errno == EAGAIN || - errno == EWOULDBLOCK) - goto out; - else + p = dbus_address_entry_get_value (entry, t); + if (!p) { - _dbus_verbose ("Error writing to remote app: %s\n", - _dbus_strerror (errno)); - do_io_error (transport); - goto out; + if (i == 0) + /* If argv0 isn't specified, fill in the path instead */ + p = path; + else + break; } - } - else - { - _dbus_verbose (" wrote %d bytes of %d\n", bytes_written, - total_bytes_to_write); - - total += bytes_written; - unix_transport->message_bytes_written += bytes_written; - _dbus_assert (unix_transport->message_bytes_written <= - total_bytes_to_write); - - if (unix_transport->message_bytes_written == total_bytes_to_write) + argv[i] = _dbus_strdup (p); + if (!argv[i]) { - unix_transport->message_bytes_written = 0; - _dbus_string_set_length (&unix_transport->encoded_outgoing, 0); - - _dbus_connection_message_sent (transport->connection, - message); + dbus_free_string_array (argv); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; } } - } - - out: - if (oom) - return FALSE; - else - return TRUE; -} - -/* returns false on out-of-memory */ -static dbus_bool_t -do_reading (DBusTransport *transport) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - DBusString *buffer; - int bytes_read; - int total; - dbus_bool_t oom; - - /* No messages without authentication! */ - if (!_dbus_transport_get_is_authenticated (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 (!dbus_watch_get_enabled (unix_transport->read_watch)) - return TRUE; - - if (total > unix_transport->max_bytes_read_per_iteration) - { - _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n", - total, unix_transport->max_bytes_read_per_iteration); - goto out; - } - - if (transport->disconnected) - goto out; - - if (_dbus_auth_needs_decoding (transport->auth)) - { - if (_dbus_string_get_length (&unix_transport->encoded_incoming) > 0) - bytes_read = _dbus_string_get_length (&unix_transport->encoded_incoming); - else - bytes_read = _dbus_read (unix_transport->fd, - &unix_transport->encoded_incoming, - unix_transport->max_bytes_read_per_iteration); - - _dbus_assert (_dbus_string_get_length (&unix_transport->encoded_incoming) == - bytes_read); - - if (bytes_read > 0) - { - int orig_len; - - _dbus_message_loader_get_buffer (transport->loader, - &buffer); - orig_len = _dbus_string_get_length (buffer); - - if (!_dbus_auth_decode_data (transport->auth, - &unix_transport->encoded_incoming, - buffer)) - { - _dbus_verbose ("Out of memory decoding incoming data\n"); - oom = TRUE; - goto out; - } - - _dbus_message_loader_return_buffer (transport->loader, - buffer, - _dbus_string_get_length (buffer) - orig_len); - - _dbus_string_set_length (&unix_transport->encoded_incoming, 0); - } - } - else - { - _dbus_message_loader_get_buffer (transport->loader, - &buffer); - - bytes_read = _dbus_read (unix_transport->fd, - buffer, unix_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 */ + *transport_p = _dbus_transport_new_for_exec (path, argv, error); + dbus_free_string_array (argv); - if (errno == ENOMEM) + if (*transport_p == NULL) { - _dbus_verbose ("Out of memory in read()/do_reading()\n"); - oom = TRUE; - goto out; + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; } - else if (errno == EAGAIN || - errno == EWOULDBLOCK) - goto out; else { - _dbus_verbose ("Error reading from remote app: %s\n", - _dbus_strerror (errno)); - do_io_error (transport); - goto out; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_OK; } } - else if (bytes_read == 0) +#ifdef DBUS_ENABLE_LAUNCHD + else if (strcmp (method, "launchd") == 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; + DBusError tmp_error = DBUS_ERROR_INIT; + const char *launchd_env_var = dbus_address_entry_get_value (entry, "env"); + const char *launchd_socket; + DBusString socket_path; + dbus_bool_t valid_socket; - if (_dbus_transport_queue_messages (transport) == DBUS_DISPATCH_NEED_MEMORY) + if (!_dbus_string_init (&socket_path)) { - oom = TRUE; - goto out; + _DBUS_SET_OOM (error); + return FALSE; } - - /* 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; -} - -static dbus_bool_t -unix_handle_watch (DBusTransport *transport, - DBusWatch *watch, - unsigned int flags) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - - _dbus_assert (watch == unix_transport->read_watch || - watch == unix_transport->write_watch); - - if (flags & (DBUS_WATCH_HANGUP | DBUS_WATCH_ERROR)) - { - _dbus_transport_disconnect (transport); - return TRUE; - } - - if (watch == unix_transport->read_watch && - (flags & DBUS_WATCH_READABLE)) - { -#if 0 - _dbus_verbose ("handling read watch\n"); -#endif - if (!do_authentication (transport, TRUE, FALSE)) - return FALSE; - - if (!do_reading (transport)) - return FALSE; - } - else if (watch == unix_transport->write_watch && - (flags & DBUS_WATCH_WRITABLE)) - { -#if 0 - _dbus_verbose ("handling write watch\n"); -#endif - if (!do_authentication (transport, FALSE, TRUE)) - return FALSE; - - if (!do_writing (transport)) - return FALSE; - } - - return TRUE; -} - -static void -unix_disconnect (DBusTransport *transport) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - - free_watches (transport); - - _dbus_close (unix_transport->fd, NULL); - unix_transport->fd = -1; -} - -static dbus_bool_t -unix_connection_set (DBusTransport *transport) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - - if (!_dbus_connection_add_watch (transport->connection, - unix_transport->write_watch)) - return FALSE; - - if (!_dbus_connection_add_watch (transport->connection, - unix_transport->read_watch)) - { - _dbus_connection_remove_watch (transport->connection, - unix_transport->write_watch); - return FALSE; - } - - check_read_watch (transport); - check_write_watch (transport); - - return TRUE; -} - -static void -unix_messages_pending (DBusTransport *transport, - int messages_pending) -{ - 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 - * we're not currently serving. Otherwise a call that just reads - * could block a write call forever (if there are no incoming - * messages). - */ -static void -unix_do_iteration (DBusTransport *transport, - unsigned int flags, - int timeout_milliseconds) -{ - DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; - fd_set read_set; - fd_set write_set; - dbus_bool_t do_select; - int select_res; - - _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p\n", - flags & DBUS_ITERATION_DO_READING ? "read" : "", - flags & DBUS_ITERATION_DO_WRITING ? "write" : "", - timeout_milliseconds, - 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, - * we don't want to read any messages yet if not given DO_READING. - * - * Also, if read_watch == NULL or write_watch == NULL, we don't - * want to read/write so don't. - */ - - FD_ZERO (&read_set); - FD_ZERO (&write_set); - - 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; - } - - if (unix_transport->write_watch && - (flags & DBUS_ITERATION_DO_WRITING)) + if (launchd_env_var == NULL) { - FD_SET (unix_transport->fd, &write_set); - do_select = TRUE; + _dbus_set_bad_address (error, "launchd", "env", NULL); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; } - } - else - { - DBusAuthState auth_state; - - auth_state = _dbus_auth_do_work (transport->auth); - if (transport->receive_credentials_pending || - auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT) - { - FD_SET (unix_transport->fd, &read_set); - do_select = TRUE; - } + valid_socket = _dbus_lookup_launchd_socket (&socket_path, launchd_env_var, error); - if (transport->send_credentials_pending || - auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND) + if (dbus_error_is_set(error)) { - FD_SET (unix_transport->fd, &write_set); - do_select = TRUE; + _dbus_string_free(&socket_path); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; } - } - if (do_select) - { - 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 (!valid_socket) { - 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 */ - } - } - else - { - /* 0 timeout to not block */ - timeout.tv_sec = 0; - timeout.tv_usec = 0; - use_timeout = TRUE; + dbus_set_error(&tmp_error, DBUS_ERROR_BAD_ADDRESS, + "launchd's env var %s does not exist", launchd_env_var); + dbus_error_free(error); + dbus_move_error(&tmp_error, error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; } - /* For blocking selects we drop the connection lock here - * to avoid blocking out connection access during a potentially - * indefinite blocking call. The io path is still protected - * by the io_path_cond condvar, so we won't reenter this. - */ - 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); + launchd_socket = _dbus_string_get_const_data(&socket_path); + *transport_p = _dbus_transport_new_for_domain_socket (launchd_socket, FALSE, error); - if (flags & DBUS_ITERATION_BLOCK) - _dbus_connection_lock (transport->connection); - - - if (select_res >= 0) + if (*transport_p == NULL) { - if (FD_ISSET (unix_transport->fd, &err_set)) - 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_verbose ("in iteration, need_read=%d need_write=%d\n", - need_read, need_write); - do_authentication (transport, need_read, need_write); - - if (need_read && (flags & DBUS_ITERATION_DO_READING)) - do_reading (transport); - if (need_write && (flags & DBUS_ITERATION_DO_WRITING)) - do_writing (transport); - } + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; } - else if (errno == EINTR) - goto again; else { - _dbus_verbose ("Error from select(): %s\n", - _dbus_strerror (errno)); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_OK; } } +#endif + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_NOT_HANDLED; + } } -static void -unix_live_messages_changed (DBusTransport *transport) -{ - /* See if we should look for incoming messages again */ - check_read_watch (transport); -} - -static DBusTransportVTable unix_vtable = { - unix_finalize, - unix_handle_watch, - unix_disconnect, - unix_connection_set, - unix_messages_pending, - unix_do_iteration, - unix_live_messages_changed -}; - -/** - * Creates a new transport for the given file descriptor. The file - * descriptor must be nonblocking (use _dbus_set_fd_nonblocking() to - * make it so). This function is shared by various transports that - * boil down to a full duplex file descriptor. - * - * @param fd the file descriptor. - * @param server #TRUE if this transport is on the server side of a connection - * @param address the transport's address - * @returns the new transport, or #NULL if no memory. - */ -DBusTransport* -_dbus_transport_new_for_fd (int fd, - dbus_bool_t server, - const DBusString *address) -{ - DBusTransportUnix *unix_transport; - - unix_transport = dbus_new0 (DBusTransportUnix, 1); - if (unix_transport == NULL) - return NULL; - - if (!_dbus_string_init (&unix_transport->encoded_outgoing)) - goto failed_0; - - if (!_dbus_string_init (&unix_transport->encoded_incoming)) - goto failed_1; - - unix_transport->write_watch = _dbus_watch_new (fd, - DBUS_WATCH_WRITABLE, - FALSE); - if (unix_transport->write_watch == NULL) - goto failed_2; - - unix_transport->read_watch = _dbus_watch_new (fd, - DBUS_WATCH_READABLE, - FALSE); - if (unix_transport->read_watch == NULL) - goto failed_3; - - if (!_dbus_transport_init_base (&unix_transport->base, - &unix_vtable, - server, address)) - goto failed_4; - - unix_transport->fd = fd; - unix_transport->message_bytes_written = 0; - - /* These values should probably be tunable or something. */ - unix_transport->max_bytes_read_per_iteration = 2048; - unix_transport->max_bytes_written_per_iteration = 2048; - - return (DBusTransport*) unix_transport; +/** @} */ - failed_4: - _dbus_watch_unref (unix_transport->read_watch); - failed_3: - _dbus_watch_unref (unix_transport->write_watch); - failed_2: - _dbus_string_free (&unix_transport->encoded_incoming); - failed_1: - _dbus_string_free (&unix_transport->encoded_outgoing); - failed_0: - dbus_free (unix_transport); - return NULL; -} +#ifdef DBUS_BUILD_TESTS -/** - * Creates a new transport for the given Unix domain socket - * path. This creates a client-side of a transport. - * - * @param path the path to the domain socket. - * @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, - DBusError *error) +dbus_bool_t +_dbus_transport_unix_test (void) { - int fd; - DBusTransport *transport; - DBusString address; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); + DBusConnection *c; + DBusError error; + dbus_bool_t ret; + const char *address; - if (!_dbus_string_init (&address)) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - return NULL; - } - - fd = -1; - - if (!_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); - if (fd < 0) - { - _DBUS_ASSERT_ERROR_IS_SET (error); - goto failed_0; - } - - _dbus_fd_set_close_on_exec (fd); - - _dbus_verbose ("Successfully connected to unix socket %s\n", - path); - - transport = _dbus_transport_new_for_fd (fd, FALSE, &address); - if (transport == NULL) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - goto failed_1; - } - - _dbus_string_free (&address); - - return transport; + dbus_error_init (&error); - failed_1: - _dbus_close (fd, NULL); - failed_0: - _dbus_string_free (&address); - return NULL; -} + c = dbus_connection_open ("unixexec:argv0=false,argv1=foobar,path=/bin/false", &error); + _dbus_assert (c != NULL); + _dbus_assert (!dbus_error_is_set (&error)); -/** - * Creates a new transport for the given hostname and port. - * - * @param host the host to connect to - * @param port the port to connect to - * @param error location to store reason for failure. - * @returns a new transport, or #NULL on failure. - */ -DBusTransport* -_dbus_transport_new_for_tcp_socket (const char *host, - dbus_int32_t port, - DBusError *error) -{ - int fd; - DBusTransport *transport; - DBusString address; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); + address = _dbus_connection_get_address (c); + _dbus_assert (address != NULL); - if (!_dbus_string_init (&address)) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - return NULL; - } - - if (!_dbus_string_append (&address, "tcp:host=") || - !_dbus_string_append (&address, host) || - !_dbus_string_append (&address, ",port=") || - !_dbus_string_append_int (&address, port)) - { - _dbus_string_free (&address); - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - return NULL; - } - - fd = _dbus_connect_tcp_socket (host, port, error); - if (fd < 0) - { - _DBUS_ASSERT_ERROR_IS_SET (error); - _dbus_string_free (&address); - return NULL; - } + /* Let's see if the address got parsed, reordered and formatted correctly */ + ret = strcmp (address, "unixexec:path=/bin/false,argv0=false,argv1=foobar") == 0; - _dbus_fd_set_close_on_exec (fd); - - _dbus_verbose ("Successfully connected to tcp socket %s:%d\n", - host, port); - - transport = _dbus_transport_new_for_fd (fd, FALSE, &address); - if (transport == NULL) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - _dbus_close (fd, NULL); - _dbus_string_free (&address); - fd = -1; - } + dbus_connection_unref (c); - _dbus_string_free (&address); - - return transport; + return ret; } -/** @} */ - +#endif