| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
| /* dbus-server-unix.c Server implementation for Unix network protocols. |
| * |
| * Copyright (C) 2002, 2003, 2004 Red Hat Inc. |
| * |
| * 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 |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #include <config.h> |
| #include "dbus-internals.h" |
| #include "dbus-server-unix.h" |
| #include "dbus-server-socket.h" |
| #include "dbus-server-launchd.h" |
| #include "dbus-transport-unix.h" |
| #include "dbus-connection-internal.h" |
| #include "dbus-sysdeps-unix.h" |
| #include "dbus-string.h" |
| |
| /** |
| * @defgroup DBusServerUnix DBusServer implementations for UNIX |
| * @ingroup DBusInternals |
| * @brief Implementation details of DBusServer on UNIX |
| * |
| * @{ |
| */ |
| |
| /** |
| * Tries to interpret the address entry in a platform-specific |
| * way, creating a platform-specific server type if appropriate. |
| * Sets error if the result is not OK. |
| * |
| * @param entry an address entry |
| * @param server_p location to store a new DBusServer, or #NULL on failure. |
| * @param error location to store rationale for failure on bad address |
| * @returns the outcome |
| * |
| */ |
| DBusServerListenResult |
| _dbus_server_listen_platform_specific (DBusAddressEntry *entry, |
| DBusServer **server_p, |
| DBusError *error) |
| { |
| const char *method; |
| |
| *server_p = NULL; |
| |
| method = dbus_address_entry_get_method (entry); |
| |
| if (strcmp (method, "unix") == 0) |
| { |
| 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 (path == NULL && tmpdir == NULL && abstract == NULL) |
| { |
| _dbus_set_bad_address(error, "unix", |
| "path or tmpdir or abstract", |
| NULL); |
| return DBUS_SERVER_LISTEN_BAD_ADDRESS; |
| } |
| |
| if ((path && tmpdir) || |
| (path && abstract) || |
| (tmpdir && abstract)) |
| { |
| _dbus_set_bad_address(error, NULL, NULL, |
| "cannot specify two of \"path\" and \"tmpdir\" and \"abstract\" at the same time"); |
| return DBUS_SERVER_LISTEN_BAD_ADDRESS; |
| } |
| |
| if (tmpdir != NULL) |
| { |
| DBusString full_path; |
| DBusString filename; |
| |
| if (!_dbus_string_init (&full_path)) |
| { |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; |
| } |
| |
| if (!_dbus_string_init (&filename)) |
| { |
| _dbus_string_free (&full_path); |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; |
| } |
| |
| if (!_dbus_string_append (&filename, |
| "dbus-") || |
| !_dbus_generate_random_ascii (&filename, 10) || |
| !_dbus_string_append (&full_path, tmpdir) || |
| !_dbus_concat_dir_and_file (&full_path, &filename)) |
| { |
| _dbus_string_free (&full_path); |
| _dbus_string_free (&filename); |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; |
| } |
| |
| /* Always use abstract namespace if possible with tmpdir */ |
| |
| *server_p = |
| _dbus_server_new_for_domain_socket (_dbus_string_get_const_data (&full_path), |
| #ifdef HAVE_ABSTRACT_SOCKETS |
| TRUE, |
| #else |
| FALSE, |
| #endif |
| error); |
| |
| _dbus_string_free (&full_path); |
| _dbus_string_free (&filename); |
| } |
| else |
| { |
| if (path) |
| *server_p = _dbus_server_new_for_domain_socket (path, FALSE, error); |
| else |
| *server_p = _dbus_server_new_for_domain_socket (abstract, TRUE, error); |
| } |
| |
| if (*server_p != NULL) |
| { |
| _DBUS_ASSERT_ERROR_IS_CLEAR(error); |
| return DBUS_SERVER_LISTEN_OK; |
| } |
| else |
| { |
| _DBUS_ASSERT_ERROR_IS_SET(error); |
| return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; |
| } |
| } |
| else if (strcmp (method, "systemd") == 0) |
| { |
| int n, *fds; |
| DBusString address; |
| |
| n = _dbus_listen_systemd_sockets (&fds, error); |
| if (n < 0) |
| { |
| _DBUS_ASSERT_ERROR_IS_SET (error); |
| return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; |
| } |
| |
| _dbus_string_init_const (&address, "systemd:"); |
| |
| *server_p = _dbus_server_new_for_socket (fds, n, &address, NULL); |
| if (*server_p == NULL) |
| { |
| int i; |
| |
| for (i = 0; i < n; i++) |
| { |
| _dbus_close_socket (fds[i], NULL); |
| } |
| dbus_free (fds); |
| |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; |
| } |
| |
| dbus_free (fds); |
| |
| return DBUS_SERVER_LISTEN_OK; |
| } |
| #ifdef DBUS_ENABLE_LAUNCHD |
| else if (strcmp (method, "launchd") == 0) |
| { |
| const char *launchd_env_var = dbus_address_entry_get_value (entry, "env"); |
| if (launchd_env_var == NULL) |
| { |
| _dbus_set_bad_address (error, "launchd", "env", NULL); |
| return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; |
| } |
| *server_p = _dbus_server_new_for_launchd (launchd_env_var, error); |
| |
| if (*server_p != NULL) |
| { |
| _DBUS_ASSERT_ERROR_IS_CLEAR(error); |
| return DBUS_SERVER_LISTEN_OK; |
| } |
| else |
| { |
| _DBUS_ASSERT_ERROR_IS_SET(error); |
| return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; |
| } |
| } |
| #endif |
| else |
| { |
| /* If we don't handle the method, we return NULL with the |
| * error unset |
| */ |
| _DBUS_ASSERT_ERROR_IS_CLEAR(error); |
| return DBUS_SERVER_LISTEN_NOT_HANDLED; |
| } |
| } |
| |
| /** |
| * Creates a new server listening on the given Unix domain socket. |
| * |
| * @param path the path for the domain socket. |
| * @param abstract #TRUE to use abstract socket namespace |
| * @param error location to store reason for failure. |
| * @returns the new server, or #NULL on failure. |
| */ |
| DBusServer* |
| _dbus_server_new_for_domain_socket (const char *path, |
| dbus_bool_t abstract, |
| DBusError *error) |
| { |
| DBusServer *server; |
| int listen_fd; |
| DBusString address; |
| char *path_copy; |
| DBusString path_str; |
| |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| |
| if (!_dbus_string_init (&address)) |
| { |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| return NULL; |
| } |
| |
| _dbus_string_init_const (&path_str, path); |
| if ((abstract && |
| !_dbus_string_append (&address, "unix:abstract=")) || |
| (!abstract && |
| !_dbus_string_append (&address, "unix:path=")) || |
| !_dbus_address_append_escaped (&address, &path_str)) |
| { |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| goto failed_0; |
| } |
| |
| if (abstract) |
| { |
| path_copy = NULL; |
| } |
| else |
| { |
| path_copy = _dbus_strdup (path); |
| if (path_copy == NULL) |
| { |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| goto failed_0; |
| } |
| } |
| |
| listen_fd = _dbus_listen_unix_socket (path, abstract, error); |
| |
| if (listen_fd < 0) |
| { |
| _DBUS_ASSERT_ERROR_IS_SET (error); |
| goto failed_1; |
| } |
| |
| server = _dbus_server_new_for_socket (&listen_fd, 1, &address, 0); |
| if (server == NULL) |
| { |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| goto failed_2; |
| } |
| |
| if (path_copy != NULL) |
| _dbus_server_socket_own_filename(server, path_copy); |
| |
| _dbus_string_free (&address); |
| |
| return server; |
| |
| failed_2: |
| _dbus_close_socket (listen_fd, NULL); |
| failed_1: |
| dbus_free (path_copy); |
| failed_0: |
| _dbus_string_free (&address); |
| |
| return NULL; |
| } |
| |
| /** @} */ |