From 041b0767b284034aee09e9a0de2a3844b8cc546a Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Mon, 25 Nov 2002 05:13:09 +0000 Subject: 2002-11-24 Havoc Pennington * test/echo-client.c, test/echo-server.c: cheesy test clients. * configure.in (AC_CHECK_FUNCS): check for writev * dbus/dbus-message.c (_dbus_message_get_network_data): new function * dbus/dbus-list.c (_dbus_list_foreach): new function * dbus/dbus-internals.c (_dbus_verbose): new function * dbus/dbus-server.c, dbus/dbus-server.h: public object representing a server that listens for connections. * dbus/.cvsignore: create * dbus/dbus-errors.h, dbus/dbus-errors.c: public API for reporting errors * dbus/dbus-connection.h, dbus/dbus-connection.c: public object representing a connection that sends/receives messages. (Same object used for both client and server.) * dbus/dbus-transport.h, dbus/dbus-transport.c: Basic abstraction for different kinds of stream that we might read/write messages from. --- dbus/dbus-server-unix.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 dbus/dbus-server-unix.c (limited to 'dbus/dbus-server-unix.c') diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c new file mode 100644 index 00000000..277e00a6 --- /dev/null +++ b/dbus/dbus-server-unix.c @@ -0,0 +1,303 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-server-unix.c Server implementation for Unix network protocols. + * + * Copyright (C) 2002 Red Hat Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-server-unix.h" +#include "dbus-transport-unix.h" +#include "dbus-connection-internal.h" +#include +#include +#include +#include +#include +#include + +/** + * @defgroup DBusServerUnix DBusServer implementations for UNIX + * @ingroup DBusInternals + * @brief Implementation details of DBusServer on UNIX + * + * @{ + */ +/** + * + * Opaque object representing a Unix server implementation. + */ +typedef struct DBusServerUnix DBusServerUnix; + +/** + * Implementation details of DBusServerUnix. All members + * are private. + */ +struct DBusServerUnix +{ + DBusServer base; /**< Parent class members. */ + int fd; /**< File descriptor or -1 if disconnected. */ + DBusWatch *watch; /**< File descriptor watch. */ +}; + +static void +unix_finalize (DBusServer *server) +{ + DBusServerUnix *unix_server = (DBusServerUnix*) server; + + _dbus_server_finalize_base (server); + + if (unix_server->watch) + _dbus_watch_unref (unix_server->watch); + + dbus_free (server); +} + +static void +handle_new_client_fd (DBusServer *server, + int client_fd) +{ + DBusConnection *connection; + DBusTransport *transport; + + _dbus_verbose ("Creating new client connection with fd %d\n", client_fd); + + if (!_dbus_set_fd_nonblocking (client_fd, NULL)) + return; + + transport = _dbus_transport_new_for_fd (client_fd); + if (transport == NULL) + { + close (client_fd); + return; + } + + /* note that client_fd is now owned by the transport, and will be + * closed on transport disconnection/finalization + */ + + connection = _dbus_connection_new_for_transport (transport); + _dbus_transport_unref (transport); + + if (connection == NULL) + return; + + /* See if someone wants to handle this new connection, + * self-referencing for paranoia + */ + if (server->new_connection_function) + { + dbus_server_ref (server); + + (* server->new_connection_function) (server, connection, + server->new_connection_data); + dbus_server_unref (server); + } + + /* If no one grabbed a reference, the connection will die. */ + dbus_connection_unref (connection); +} + +static void +unix_handle_watch (DBusServer *server, + DBusWatch *watch, + unsigned int flags) +{ + DBusServerUnix *unix_server = (DBusServerUnix*) server; + + _dbus_assert (watch == unix_server->watch); + + _dbus_verbose ("Handling client connection, flags 0x%x\n", flags); + + if (flags & DBUS_WATCH_READABLE) + { + int client_fd; + int listen_fd; + + listen_fd = dbus_watch_get_fd (watch); + + retry: + client_fd = accept (listen_fd, NULL, NULL); + + if (client_fd < 0) + { + if (errno == EINTR) + goto retry; + else if (errno == EAGAIN || errno == EWOULDBLOCK) + _dbus_verbose ("No client available to accept after all\n"); + else + _dbus_verbose ("Failed to accept a client connection: %s\n", + _dbus_strerror (errno)); + } + else + { + handle_new_client_fd (server, client_fd); + } + } + + if (flags & DBUS_WATCH_ERROR) + _dbus_verbose ("Error on server listening socket\n"); + + if (flags & DBUS_WATCH_HANGUP) + _dbus_verbose ("Hangup on server listening socket\n"); +} + +static void +unix_disconnect (DBusServer *server) +{ + DBusServerUnix *unix_server = (DBusServerUnix*) server; + + if (unix_server->watch) + { + _dbus_server_remove_watch (server, + unix_server->watch); + _dbus_watch_unref (unix_server->watch); + unix_server->watch = NULL; + } + + close (unix_server->fd); + unix_server->fd = -1; +} + +static DBusServerVTable unix_vtable = { + unix_finalize, + unix_handle_watch, + unix_disconnect +}; + +/** + * Creates a new server listening on the given file descriptor. The + * file descriptor should be nonblocking (use + * _dbus_set_fd_nonblocking() to make it so). The file descriptor + * should be listening for connections, that is, listen() should have + * been successfully invoked on it. The server will use accept() to + * accept new client connections. + * + * @param fd the file descriptor. + * @returns the new server, or #NULL if no memory. + * + */ +DBusServer* +_dbus_server_new_for_fd (int fd) +{ + DBusServerUnix *unix_server; + DBusWatch *watch; + + watch = _dbus_watch_new (fd, + DBUS_WATCH_READABLE); + if (watch == NULL) + return NULL; + + unix_server = dbus_new0 (DBusServerUnix, 1); + if (unix_server == NULL) + { + _dbus_watch_unref (watch); + return NULL; + } + + if (!_dbus_server_init_base (&unix_server->base, + &unix_vtable)) + { + _dbus_watch_unref (watch); + dbus_free (unix_server); + return NULL; + } + + if (!_dbus_server_add_watch (&unix_server->base, + watch)) + { + _dbus_server_finalize_base (&unix_server->base); + _dbus_watch_unref (watch); + dbus_free (unix_server); + return NULL; + } + + unix_server->fd = fd; + unix_server->watch = watch; + + return (DBusServer*) unix_server; +} + +/** + * Creates a new server listening on the given Unix domain socket. + * + * @param path the path for the domain socket. + * @param result location to store reason for failure. + * @returns the new server, or #NULL on failure. + */ +DBusServer* +_dbus_server_new_for_domain_socket (const char *path, + DBusResultCode *result) +{ + DBusServer *server; + int listen_fd; + struct sockaddr_un addr; + + listen_fd = socket (AF_LOCAL, SOCK_STREAM, 0); + + if (listen_fd < 0) + { + dbus_set_result (result, _dbus_result_from_errno (errno)); + _dbus_verbose ("Failed to create socket \"%s\": %s\n", + path, _dbus_strerror (errno)); + return NULL; + } + + if (!_dbus_set_fd_nonblocking (listen_fd, result)) + { + close (listen_fd); + return NULL; + } + + _DBUS_ZERO (addr); + addr.sun_family = AF_LOCAL; + strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH); + addr.sun_path[_DBUS_MAX_SUN_PATH_LENGTH] = '\0'; + + if (bind (listen_fd, (struct sockaddr*) &addr, SUN_LEN (&addr)) < 0) + { + dbus_set_result (result, _dbus_result_from_errno (errno)); + _dbus_verbose ("Failed to bind socket \"%s\": %s\n", + path, _dbus_strerror (errno)); + close (listen_fd); + return NULL; + } + + if (listen (listen_fd, 30 /* backlog */) < 0) + { + dbus_set_result (result, _dbus_result_from_errno (errno)); + _dbus_verbose ("Failed to listen on socket \"%s\": %s\n", + path, _dbus_strerror (errno)); + close (listen_fd); + return NULL; + } + + server = _dbus_server_new_for_fd (listen_fd); + if (server == NULL) + { + dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + close (listen_fd); + return NULL; + } + + return server; +} + +/** @} */ + -- cgit