From c92339de11a2f27198aee3b4242aa6fccc12a004 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sat, 4 Jan 2003 20:29:46 +0000 Subject: 2003-01-04 Havoc Pennington * dbus/dbus-sysdeps.c (_dbus_sleep_milliseconds): new function (_dbus_poll): new function * dbus/dbus-internals.h (_DBUS_STRUCT_OFFSET): new macro copied from GLib * bus/loop.c: initial code for the daemon main loop --- ChangeLog | 10 +++ bus/Makefile.am | 7 +- bus/loop.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++ bus/loop.h | 44 ++++++++++ bus/main.c | 161 ++++++++++++++++++++++++++++++++++++ configure.in | 2 +- dbus/dbus-internals.h | 12 +++ dbus/dbus-sysdeps.c | 94 ++++++++++++++++++++- dbus/dbus-sysdeps.h | 22 +++++ 9 files changed, 572 insertions(+), 4 deletions(-) create mode 100644 bus/loop.c create mode 100644 bus/loop.h diff --git a/ChangeLog b/ChangeLog index 3008d92c..059b24e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2003-01-04 Havoc Pennington + + * dbus/dbus-sysdeps.c (_dbus_sleep_milliseconds): new function + (_dbus_poll): new function + + * dbus/dbus-internals.h (_DBUS_STRUCT_OFFSET): new macro + copied from GLib + + * bus/loop.c: initial code for the daemon main loop + 2003-01-04 Havoc Pennington * test/watch.c (error_handler): make it safe if the error handler diff --git a/bus/Makefile.am b/bus/Makefile.am index da115c4d..f51d8950 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -1,15 +1,18 @@ INCLUDES=-I$(top_srcdir) $(DBUS_BUS_CFLAGS) \ - -DDAEMON_NAME=\"dbus-daemon-1\" + -DDAEMON_NAME=\"dbus-daemon-1\" -DDBUS_COMPILATION EFENCE= bin_PROGRAMS=dbus-daemon-1 dbus_daemon_1_SOURCES= \ + loop.c \ + loop.h \ main.c dbus_daemon_1_LDADD= \ $(EFENCE) \ $(DBUS_BUS_LIBS) \ - $(top_builddir)/dbus/libdbus-convenience.la + $(top_builddir)/dbus/libdbus-convenience.la \ + $(top_builddir)/dbus/libdbus-1.la diff --git a/bus/loop.c b/bus/loop.c new file mode 100644 index 00000000..633ea42a --- /dev/null +++ b/bus/loop.c @@ -0,0 +1,224 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* loop.c Main loop for daemon + * + * Copyright (C) 2003 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 "loop.h" +#include +#include + +static DBusList *watches = NULL; +static int watch_list_serial = 0; +static dbus_bool_t exited = FALSE; + +typedef struct +{ + DBusWatch *watch; + BusWatchFunction function; + void *data; + DBusFreeFunction free_data_func; +} WatchCallback; + +dbus_bool_t +bus_loop_add_watch (DBusWatch *watch, + BusWatchFunction function, + void *data, + DBusFreeFunction free_data_func) +{ + WatchCallback *cb; + + cb = dbus_new (WatchCallback, 1); + if (cb == NULL) + return FALSE; + + cb->watch = watch; + cb->function = function; + cb->data = data; + cb->free_data_func = free_data_func; + + if (!_dbus_list_append (&watches, cb)) + { + dbus_free (cb); + return FALSE; + } + + watch_list_serial += 1; + + return TRUE; +} + +void +bus_loop_remove_watch (DBusWatch *watch, + BusWatchFunction function, + void *data) +{ + DBusList *link; + + link = _dbus_list_get_first_link (&watches); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&watches, link); + WatchCallback *cb = link->data; + + if (cb->watch == watch && + cb->function == function && + cb->data == data) + { + _dbus_list_remove_link (&watches, link); + + watch_list_serial += 1; + + if (cb->free_data_func) + (* cb->free_data_func) (cb->data); + dbus_free (cb); + + return; + } + + link = next; + } + + _dbus_warn ("could not find watch %p function %p data %p to remove\n", + watch, function, data); +} + +static void +wait_for_memory (void) +{ + _dbus_sleep_milliseconds (500); +} + +void +bus_loop_run (void) +{ + while (!exited) + { + DBusPollFD *fds; + int n_fds; + WatchCallback **watches_for_fds; + int i; + DBusList *link; + int n_ready; + int initial_serial; + + fds = NULL; + watches_for_fds = NULL; + + n_fds = _dbus_list_get_length (&watches); + + if (n_fds == 0) + { + bus_loop_quit (); + goto next_iteration; + } + + fds = dbus_new0 (DBusPollFD, n_fds); + while (fds == NULL) + { + wait_for_memory (); + fds = dbus_new0 (DBusPollFD, n_fds); + } + + watches_for_fds = dbus_new (WatchCallback*, n_fds); + while (watches_for_fds == NULL) + { + wait_for_memory (); + watches_for_fds = dbus_new (WatchCallback*, n_fds); + } + + i = 0; + link = _dbus_list_get_first_link (&watches); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&watches, link); + WatchCallback *cb = link->data; + int flags; + + watches_for_fds[i] = cb; + + flags = dbus_watch_get_flags (cb->watch); + + fds[i].fd = dbus_watch_get_fd (cb->watch); + if (flags & DBUS_WATCH_READABLE) + fds[i].events |= _DBUS_POLLIN; + if (flags & DBUS_WATCH_WRITABLE) + fds[i].events |= _DBUS_POLLOUT; + + link = next; + ++i; + } + + n_ready = _dbus_poll (fds, n_fds, -1); + + if (n_ready > 0) + { + initial_serial = watch_list_serial; + i = 0; + while (i < n_fds) + { + if (initial_serial != watch_list_serial) + goto next_iteration; + + if (exited) + goto next_iteration; + + if (fds[i].revents != 0) + { + WatchCallback *cb; + unsigned int condition; + + cb = watches_for_fds[i]; + + condition = 0; + if (fds[i].revents & _DBUS_POLLIN) + condition |= DBUS_WATCH_READABLE; + if (fds[i].revents & _DBUS_POLLOUT) + condition |= DBUS_WATCH_WRITABLE; + if (fds[i].revents & _DBUS_POLLHUP) + condition |= DBUS_WATCH_HANGUP; + if (fds[i].revents & _DBUS_POLLERR) + condition |= DBUS_WATCH_ERROR; + + /* condition may still be 0 if we got some + * weird POLLFOO thing like POLLWRBAND + */ + + if (condition != 0) + (* cb->function) (cb->watch, + condition, + cb->data); + } + + ++i; + } + } + + next_iteration: + dbus_free (fds); + dbus_free (watches_for_fds); + } +} + +void +bus_loop_quit (void) +{ + exited = TRUE; +} diff --git a/bus/loop.h b/bus/loop.h new file mode 100644 index 00000000..e2b5e50a --- /dev/null +++ b/bus/loop.h @@ -0,0 +1,44 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* loop.h Main loop for daemon + * + * Copyright (C) 2003 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 + * + */ + +#ifndef BUS_LOOP_H +#define BUS_LOOP_H + +#include + +typedef void (* BusWatchFunction) (DBusWatch *watch, + unsigned int condition, + void *data); + +dbus_bool_t bus_loop_add_watch (DBusWatch *watch, + BusWatchFunction function, + void *data, + DBusFreeFunction free_data_func); +void bus_loop_remove_watch (DBusWatch *watch, + BusWatchFunction function, + void *data); +void bus_loop_run (void); +void bus_loop_quit (void); + + +#endif /* BUS_LOOP_H */ diff --git a/bus/main.c b/bus/main.c index 1025a0e7..199a10f0 100644 --- a/bus/main.c +++ b/bus/main.c @@ -1,8 +1,169 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* main.c main() for message bus + * + * Copyright (C) 2003 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 "loop.h" +#include +#include + +static DBusList *connections = NULL; + +static void +connection_error_handler (DBusConnection *connection, + DBusResultCode error_code, + void *data) +{ + _dbus_warn ("Error on connection: %s\n", + dbus_result_to_string (error_code)); + + /* we don't want to be called again since we're dropping the connection */ + dbus_connection_set_error_function (connection, NULL, NULL, NULL); + + _dbus_list_remove (&connections, connection); + dbus_connection_unref (connection); +} + +static void +connection_watch_callback (DBusWatch *watch, + unsigned int condition, + void *data) +{ + DBusConnection *connection = data; + + dbus_connection_handle_watch (connection, watch, condition); +} + +static void +server_watch_callback (DBusWatch *watch, + unsigned int condition, + void *data) +{ + DBusServer *server = data; + + dbus_server_handle_watch (server, watch, condition); +} + +static void +add_connection_watch (DBusWatch *watch, + DBusConnection *connection) +{ + bus_loop_add_watch (watch, connection_watch_callback, connection, + NULL); +} + +static void +remove_connection_watch (DBusWatch *watch, + DBusConnection *connection) +{ + bus_loop_remove_watch (watch, connection_watch_callback, connection); +} + +static void +add_server_watch (DBusWatch *watch, + DBusServer *server) +{ + bus_loop_add_watch (watch, server_watch_callback, server, + NULL); +} + +static void +remove_server_watch (DBusWatch *watch, + DBusServer *server) +{ + bus_loop_remove_watch (watch, server_watch_callback, server); +} + +static dbus_bool_t +setup_connection (DBusConnection *connection) +{ + if (!_dbus_list_append (&connections, connection)) + { + dbus_connection_disconnect (connection); + return FALSE; + } + + dbus_connection_ref (connection); + + dbus_connection_set_watch_functions (connection, + (DBusAddWatchFunction) add_connection_watch, + (DBusRemoveWatchFunction) remove_connection_watch, + connection, + NULL); + + dbus_connection_set_error_function (connection, + connection_error_handler, + NULL, NULL); + + return TRUE; +} + +static void +setup_server (DBusServer *server) +{ + dbus_server_set_watch_functions (server, + (DBusAddWatchFunction) add_server_watch, + (DBusRemoveWatchFunction) remove_server_watch, + server, + NULL); +} + +static void +new_connection_callback (DBusServer *server, + DBusConnection *new_connection, + void *data) +{ + if (!setup_connection (new_connection)) + ; /* we won't have ref'd the connection so it will die */ +} int main (int argc, char **argv) { + DBusServer *server; + DBusResultCode result; + + if (argc < 2) + { + _dbus_warn ("Give the server address as an argument\n"); + return 1; + } + + server = dbus_server_listen (argv[1], &result); + if (server == NULL) + { + _dbus_warn ("Failed to start server on %s: %s\n", + argv[1], dbus_result_to_string (result)); + return 1; + } + + setup_server (server); + dbus_server_set_new_connection_function (server, + new_connection_callback, + NULL, NULL); + + bus_loop_run (); + + dbus_server_disconnect (server); + dbus_server_unref (server); return 0; } diff --git a/configure.in b/configure.in index 9435c9de..68dd9ba5 100644 --- a/configure.in +++ b/configure.in @@ -101,7 +101,7 @@ AC_CHECK_SIZEOF(__int64) ## byte order AC_C_BIGENDIAN -AC_CHECK_FUNCS(vsnprintf vasprintf getpwnam_r) +AC_CHECK_FUNCS(vsnprintf vasprintf getpwnam_r nanosleep usleep poll) dnl check for writev header and writev function so we're dnl good to go if HAVE_WRITEV gets defined. diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 027b3ef6..2f025c14 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -69,6 +69,9 @@ do { #define _DBUS_ZERO(object) (memset (&(object), '\0', sizeof ((object)))) +#define _DBUS_STRUCT_OFFSET(struct_type, member) \ + ((long) ((unsigned char*) &((struct_type*) 0)->member)) + char* _dbus_strdup (const char *str); #define _DBUS_INT_MIN (-_DBUS_INT_MAX - 1) @@ -77,6 +80,15 @@ char* _dbus_strdup (const char *str); #define _DBUS_ONE_KILOBYTE 1024 #define _DBUS_ONE_MEGABYTE 1024 * _DBUS_ONE_KILOBYTE +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#undef ABS +#define ABS(a) (((a) < 0) ? -(a) : (a)) + typedef void (* DBusForeachFunction) (void *element, void *data); diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index f85a4a95..a4074828 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -34,10 +34,13 @@ #include #include #include +#include #ifdef HAVE_WRITEV #include #endif - +#ifdef HAVE_POLL +#include +#endif /** * @addtogroup DBusInternalsUtils @@ -891,4 +894,93 @@ _dbus_string_append_our_uid (DBusString *str) return _dbus_string_append_int (str, getuid ()); } + +/** + * Wrapper for poll(). + * + * @todo need a fallback implementation using select() + * + * @param fds the file descriptors to poll + * @param n_fds number of descriptors in the array + * @param timeout_milliseconds timeout or -1 for infinite + * @returns numbers of fds with revents, or <0 on error + */ +int +_dbus_poll (DBusPollFD *fds, + int n_fds, + int timeout_milliseconds) +{ +#ifdef HAVE_POLL + /* This big thing is a constant expression and should get optimized + * out of existence. So it's more robust than a configure check at + * no cost. + */ + if (_DBUS_POLLIN == POLLIN && + _DBUS_POLLPRI == POLLPRI && + _DBUS_POLLOUT == POLLOUT && + _DBUS_POLLERR == POLLERR && + _DBUS_POLLHUP == POLLHUP && + _DBUS_POLLNVAL == POLLNVAL && + sizeof (DBusPollFD) == sizeof (struct pollfd) && + _DBUS_STRUCT_OFFSET (DBusPollFD, fd) == + _DBUS_STRUCT_OFFSET (struct pollfd, fd) && + _DBUS_STRUCT_OFFSET (DBusPollFD, events) == + _DBUS_STRUCT_OFFSET (struct pollfd, events) && + _DBUS_STRUCT_OFFSET (DBusPollFD, revents) == + _DBUS_STRUCT_OFFSET (struct pollfd, revents)) + { + return poll ((struct pollfd*) fds, + n_fds, + timeout_milliseconds); + } + else + { + /* We have to convert the DBusPollFD to an array of + * struct pollfd, poll, and convert back. + */ + _dbus_warn ("didn't implement poll() properly for this system yet\n"); + return -1; + } +#else /* ! HAVE_POLL */ + _dbus_warn ("need to implement select() fallback for systems with no poll()\n"); + return -1; +#endif +} + +/** nanoseconds in a second */ +#define NANOSECONDS_PER_SECOND 1000000000 +/** microseconds in a second */ +#define MICROSECONDS_PER_SECOND 1000000 +/** milliseconds in a second */ +#define MILLISECONDS_PER_SECOND 1000 +/** nanoseconds in a millisecond */ +#define NANOSECONDS_PER_MILLISECOND 1000000 +/** microseconds in a millisecond */ +#define MICROSECONDS_PER_MILLISECOND 1000 + +/** + * Sleeps the given number of milliseconds. + * @param milliseconds number of milliseconds + */ +void +_dbus_sleep_milliseconds (int milliseconds) +{ +#ifdef HAVE_NANOSLEEP + struct timespec req; + struct timespec rem; + + req.tv_sec = milliseconds / MILLISECONDS_PER_SECOND; + req.tv_nsec = (milliseconds % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND; + rem.tv_sec = 0; + rem.tv_nsec = 0; + + while (nanosleep (&req, &rem) < 0 && errno == EINTR) + req = rem; +#elif defined (HAVE_USLEEP) + usleep (milliseconds * MICROSECONDS_PER_MILLISECOND); +#else /* ! HAVE_USLEEP */ + sleep (MAX (milliseconds / 1000, 1)); +#endif +} + /** @} end of sysdeps */ diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 73482f06..c76c691a 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -96,6 +96,28 @@ dbus_bool_t _dbus_credentials_match (const DBusCredentials *expec dbus_bool_t _dbus_string_append_our_uid (DBusString *str); + + +#define _DBUS_POLLIN 0x0001 /* There is data to read */ +#define _DBUS_POLLPRI 0x0002 /* There is urgent data to read */ +#define _DBUS_POLLOUT 0x0004 /* Writing now will not block */ +#define _DBUS_POLLERR 0x0008 /* Error condition */ +#define _DBUS_POLLHUP 0x0010 /* Hung up */ +#define _DBUS_POLLNVAL 0x0020 /* Invalid request: fd not open */ + +typedef struct +{ + int fd; + short events; + short revents; +} DBusPollFD; + +int _dbus_poll (DBusPollFD *fds, + int n_fds, + int timeout_milliseconds); + +void _dbus_sleep_milliseconds (int milliseconds); + DBUS_END_DECLS; #endif /* DBUS_SYSDEPS_H */ -- cgit