From dcbe55598fbf0712b9849565022d19f654866af8 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Jun 23 2015 20:42:37 +0000 Subject: Move to using per-service connections Instead of trying to guess which service the user meant to talk to, make the bus connection into a per-service item that we create and destroy as needed. When processing an incoming request, use the name that we associated with the connection instead of the destination address when we start to route the request. --- diff --git a/src/mainloop.c b/src/mainloop.c index 118b724..0863153 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -495,7 +495,7 @@ handle(fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *unused_tv) /* Connect our callbacks for the D-Bus connection. */ void -mainloop_reinit(DBusConnection *conn) +mainloop_connect(DBusConnection *conn) { dbus_connection_set_watch_functions(conn, watch_dbus_add, @@ -512,13 +512,7 @@ mainloop_reinit(DBusConnection *conn) } void -mainloop_init(DBusConnection *conn) -{ - mainloop_reinit(conn); -} - -void -mainloop_done(DBusConnection *conn) +mainloop_disconnect(DBusConnection *conn) { dbus_connection_set_watch_functions(conn, NULL, @@ -538,7 +532,7 @@ mainloop_done(DBusConnection *conn) * pending output. Return the number of descriptors which we serviced. Note * that if we got a timeout, 0 is a *successful* return code. */ int -mainloop_iterate(DBusConnection *conn) +mainloop_iterate(void) { int i, max_fd; dbus_bool_t use_tv; @@ -549,21 +543,6 @@ mainloop_iterate(DBusConnection *conn) i = select(max_fd + 1, &rfds, &wfds, &efds, use_tv ? &tv : NULL); if (i != -1) { handle(&rfds, &wfds, &efds, &tv); - while (dbus_connection_get_dispatch_status(conn) == - DBUS_DISPATCH_DATA_REMAINS) { - dbus_connection_dispatch(conn); - } -#if DBUS_CHECK_VERSION(0,30,0) - while (dbus_connection_has_messages_to_send(conn)) { - dbus_connection_flush(conn); - } -#elif DBUS_CHECK_VERSION(0,20,0) - while (dbus_connection_get_outgoing_size(conn) > 0) { - dbus_connection_flush(conn); - } -#else -#error "Don't know how to check if messages need to be flushed!" -#endif } return i; } diff --git a/src/mainloop.h b/src/mainloop.h index 4d866d0..27e0402 100644 --- a/src/mainloop.h +++ b/src/mainloop.h @@ -40,9 +40,9 @@ void mainloop_oddjob_watch_remove(int fd, DBusWatchFlags flags); dbus_bool_t mainloop_pid_add(pid_t pid, OddjobPidFn *fn, void *data); void mainloop_pid_remove(pid_t pid); void mainloop_init(DBusConnection *conn); -void mainloop_reinit(DBusConnection *conn); -void mainloop_done(DBusConnection *conn); -int mainloop_iterate(DBusConnection *conn); +void mainloop_connect(DBusConnection *conn); +void mainloop_disconnect(DBusConnection *conn); +int mainloop_iterate(void); void mainloop_reset_signal_handlers(void); #endif diff --git a/src/oddjob_dbus.c b/src/oddjob_dbus.c index 8d18869..c785d39 100644 --- a/src/oddjob_dbus.c +++ b/src/oddjob_dbus.c @@ -47,10 +47,11 @@ #include "util.h" struct oddjob_dbus_context { - DBusConnection *conn; DBusBusType bustype; - dbus_bool_t registered; + int reconnect_timeout; struct oddjob_dbus_service { + struct oddjob_dbus_context *ctx; + DBusConnection *conn; char *name; struct oddjob_dbus_object { char *path; @@ -69,7 +70,6 @@ struct oddjob_dbus_context { int n_objects; } *services; int n_services; - int reconnect_timeout; }; struct oddjob_dbus_message { @@ -81,39 +81,19 @@ struct oddjob_dbus_message { char *selinux_context; }; -static dbus_bool_t -message_has_path(DBusMessage *message, const char *path) -{ -#if DBUS_CHECK_VERSION(0,34,0) - return dbus_message_has_path(message, path); -#elif DBUS_CHECK_VERSION(0,20,0) - const char *msgpath; - msgpath = dbus_message_get_path(message); - if ((msgpath == NULL) && (path == NULL)) { - return TRUE; - } - if ((msgpath != NULL) && (path != NULL) && - (strcmp(msgpath, path) == 0)) { - return TRUE; - } - return FALSE; -#else -#error "Don't know how to check message information in your version of D-Bus!" -#endif -} - static DBusHandlerResult oddjob_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data); +/* Acquire a well-known service name. */ static dbus_bool_t oddjob_dbus_bind(DBusConnection *conn, const char *service_name) { #if DBUS_CHECK_VERSION(0,60,0) - return dbus_bus_request_name(conn, service_name, + return dbus_bus_request_name(conn, service_name, DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; #elif DBUS_CHECK_VERSION(0,30,0) - return dbus_bus_request_name(conn, service_name, + return dbus_bus_request_name(conn, service_name, DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT | DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; @@ -128,23 +108,25 @@ oddjob_dbus_bind(DBusConnection *conn, const char *service_name) #endif } +/* Release a well-known service name. */ static int oddjob_dbus_unbind(DBusConnection *conn, const char *service_name) { #if DBUS_CHECK_VERSION(0,60,0) - return dbus_bus_release_name(conn, service_name, NULL); + return dbus_bus_release_name(conn, service_name, NULL); #else #warning "Can't unregister services with this version of D-Bus." return FALSE; #endif } +/* Completely dispose of a connection to the bus. */ void oddjob_dbus_connection_close(DBusConnection *conn) { #if DBUS_CHECK_VERSION(0,34,0) - /* Apparently we abort now when we try this. */ - /* dbus_connection_close(conn); */ + dbus_connection_close(conn); + dbus_connection_unref(conn); #elif DBUS_CHECK_VERSION(0,20,0) dbus_connection_disconnect(conn); #else @@ -152,58 +134,137 @@ oddjob_dbus_connection_close(DBusConnection *conn) #endif } +/* Set our reconnect timeout, and make sure any open connections have their + * exit-on-disconnect policy updated to match. */ void -oddjob_dbus_listener_set_reconnect_timeout(struct oddjob_dbus_context *ctx, - int timeout) +oddjob_dbus_listeners_set_reconnect_timeout(struct oddjob_dbus_context *ctx, + int timeout) { - dbus_connection_set_exit_on_disconnect(ctx->conn, timeout <= 0); + struct oddjob_dbus_service *srv; + int i; + ctx->reconnect_timeout = timeout; + for (i = 0; i < ctx->n_services; i++) { + srv = &ctx->services[i]; + /* Bring open connections in line with our disconnect policy. */ + dbus_connection_set_exit_on_disconnect(srv->conn, + ctx->reconnect_timeout <= 0); + } } +/* Create a new master state structure. */ struct oddjob_dbus_context * -oddjob_dbus_listener_new(DBusBusType bustype) +oddjob_dbus_listeners_new(DBusBusType bustype) { - DBusError err; - DBusConnection *conn; struct oddjob_dbus_context *ctx; - memset(&err, 0, sizeof(err)); - conn = dbus_bus_get(bustype, &err); - if (conn == NULL) { - return NULL; - } - ctx = oddjob_malloc0(sizeof(struct oddjob_dbus_context)); if (ctx == NULL) { return NULL; } ctx->bustype = bustype; - ctx->conn = conn; - -#if ! DBUS_CHECK_VERSION(0,22,0) - /* According to johnp, we don't need to bother with any of this, but - * I've only verified it back as far as 0.22. */ - /* Request all messages which we're allowed to see. */ - dbus_bus_add_match(ctx->conn, "", &err); -#endif - - ctx->n_services = 0; ctx->reconnect_timeout = 0; + ctx->n_services = 0; ctx->services = NULL; return ctx; } +/* Open a new connection for a given service name. */ +static dbus_bool_t +service_connect(struct oddjob_dbus_service *srv) +{ + DBusError err; + DBusConnection *conn; + int attempt = 0; + + do { + dbus_error_init(&err); + conn = dbus_bus_get_private(srv->ctx->bustype, &err); + if (dbus_error_is_set(&err)) { + dbus_error_free(&err); + fprintf(stderr, "Error connecting to bus for " + "\"%s\" (attempt %d)!\n", srv->name, + attempt + 1); + } + if ((conn == NULL) || + (!dbus_connection_get_is_connected(conn))) { + /* No joy. Discard this attempt. */ + if (conn != NULL) { + oddjob_dbus_connection_close(conn); + conn = NULL; + } + /* Wait before trying again. In case it was just a + * restart, try to connect N times with our "fast" + * timeout, then fall back to the configured timeout. */ + if ((attempt < DEFAULT_FAST_RECONNECT_ATTEMPTS) && + (srv->ctx->reconnect_timeout > DEFAULT_FAST_RECONNECT_TIMEOUT)) { + sleep(DEFAULT_FAST_RECONNECT_TIMEOUT); + } else { + sleep(srv->ctx->reconnect_timeout); + } + attempt++; + } + } while (conn == NULL); + + /* Set our disconnect policy. */ + dbus_connection_set_exit_on_disconnect(conn, + srv->ctx->reconnect_timeout <= 0); + + /* Attempt to bind/rebind to the desired service name. */ + if (!oddjob_dbus_bind(conn, srv->name)) { + fprintf(stderr, "Error acquiring well-known service name " + "\"%s\"!\n", srv->name); + oddjob_dbus_connection_close(conn); + return FALSE; + } + + /* Register our filter, which does the dispatching. */ + if (!dbus_connection_add_filter(conn, oddjob_dbus_filter, + srv->ctx, NULL)) { + oddjob_dbus_connection_close(conn); + return FALSE; + } + + /* Attach the connection's descriptors to the main loop. */ + mainloop_connect(conn); + + /* Done. */ + srv->conn = conn; + return TRUE; +} + +/* Close a connection for a given service name. */ +static void +service_disconnect(struct oddjob_dbus_service *srv) +{ + if (srv->conn != NULL) { + /* Detach the connection's descriptor from the main loop. */ + mainloop_disconnect(srv->conn); + /* Remove the filter. */ + dbus_connection_remove_filter(srv->conn, oddjob_dbus_filter, + srv->ctx); + /* Release the well-known name. */ + oddjob_dbus_unbind(srv->conn, srv->name); + /* Close the connection. */ + oddjob_dbus_connection_close(srv->conn); + srv->conn = NULL; + } +} + +/* Free a master state structure. */ void -oddjob_dbus_listener_free(struct oddjob_dbus_context *ctx) +oddjob_dbus_listeners_free(struct oddjob_dbus_context *ctx) { int i, j, k, l; if (ctx == NULL) { return; } + /* Clean up the context. */ for (i = 0; i < ctx->n_services; i++) { /* Clean up this service. */ + service_disconnect(&ctx->services[i]); for (j = 0; j < ctx->services[i].n_objects; j++) { /* Clean up this object. */ for (k = 0; k < ctx->services[i].objects[j].n_interfaces; k++) { @@ -220,34 +281,29 @@ oddjob_dbus_listener_free(struct oddjob_dbus_context *ctx) } oddjob_free(ctx->services[i].objects[j].interfaces[k].methods); ctx->services[i].objects[j].interfaces[k].methods = NULL; + ctx->services[i].objects[j].interfaces[k].n_methods = 0; oddjob_free(ctx->services[i].objects[j].interfaces[k].interface); ctx->services[i].objects[j].interfaces[k].interface = NULL; } oddjob_free(ctx->services[i].objects[j].interfaces); ctx->services[i].objects[j].interfaces = NULL; + ctx->services[i].objects[j].n_interfaces = 0; oddjob_free(ctx->services[i].objects[j].path); ctx->services[i].objects[j].path = NULL; } - oddjob_free(ctx->services[i].name); - ctx->services[i].name = NULL; oddjob_free(ctx->services[i].objects); ctx->services[i].objects = NULL; + ctx->services[i].n_objects = 0; + oddjob_free(ctx->services[i].name); + ctx->services[i].name = NULL; } - /* Clean up this service. */ oddjob_free(ctx->services); ctx->services = NULL; ctx->n_services = 0; - if (ctx->registered) { - dbus_connection_remove_filter(ctx->conn, - oddjob_dbus_filter, - ctx); - ctx->registered = FALSE; - } - oddjob_dbus_connection_close(ctx->conn); - ctx->conn = NULL; oddjob_free(ctx); } +/* Fetch and cache the caller's SELinux context. */ const char * oddjob_dbus_message_get_selinux_context(struct oddjob_dbus_message *msg) { @@ -360,6 +416,7 @@ oddjob_dbus_message_set_selinux_context(struct oddjob_dbus_message *msg, } } +/* Parse options and potentially an SELinux context from a D-Bus request. */ static struct oddjob_dbus_message * oddjob_dbus_message_from_message(DBusConnection *conn, DBusMessage *message, @@ -431,7 +488,7 @@ oddjob_dbus_message_from_message(DBusConnection *conn, sender_bus_name = dbus_message_get_sender(msg->msg); if (sender_bus_name != NULL) { if (get_selinux_context) { - context_str = oddjob_dbus_get_selinux_context(msg->conn, + context_str = oddjob_dbus_get_selinux_context(msg->conn, sender_bus_name); } else { context_str = NULL; @@ -501,138 +558,53 @@ oddjob_dbus_message_free(struct oddjob_dbus_message *msg) } } +/* Reconnect any listeners which are no longer connected to the bus. */ void -oddjob_dbus_listener_reconnect_if_needed(struct oddjob_dbus_context *ctx) +oddjob_dbus_listeners_reconnect_if_needed(struct oddjob_dbus_context *ctx) { - DBusConnection *newconn; - DBusError err; - int i, attempt; - - /* If we're still connected, we don't need to do anything. */ - if (dbus_connection_get_is_connected(ctx->conn)) { - return; - } - - /* Mark that we were disconnected. */ - ctx->registered = FALSE; - - /* Close the connection, to flush out data. */ - oddjob_dbus_connection_close(ctx->conn); - - /* Unref the old connection. */ - dbus_connection_unref(ctx->conn); + struct oddjob_dbus_service *srv; + int i; - /* Get a new connection. */ - attempt = 0; - do { - dbus_error_init(&err); - newconn = dbus_bus_get(ctx->bustype, &err); - if (dbus_error_is_set(&err)) { - dbus_error_free(&err); - } - if ((newconn == NULL) || - (!dbus_connection_get_is_connected(newconn))) { - /* No joy. Discard this attempt. */ - if (newconn != NULL) { - dbus_connection_unref(newconn); - newconn = NULL; - } - /* Wait before trying again. In case it was just a - * restart, try to connect N times with our "fast" - * timeout, then fall back to the configured timeout. */ - if ((attempt < DEFAULT_FAST_RECONNECT_ATTEMPTS) && - (ctx->reconnect_timeout > DEFAULT_FAST_RECONNECT_TIMEOUT)) { - sleep(DEFAULT_FAST_RECONNECT_TIMEOUT); - attempt++; - } else { - sleep(ctx->reconnect_timeout); - } + for (i = 0; i < ctx->n_services; i++) { + srv = &ctx->services[i]; + /* If we're still connected, we don't need to do anything. */ + if ((srv->conn != NULL) && + dbus_connection_get_is_connected(srv->conn)) { + continue; } - } while (newconn == NULL); - ctx->conn = newconn; - - /* Set our reconnect policy. */ - oddjob_dbus_listener_set_reconnect_timeout(ctx, ctx->reconnect_timeout); - -#if 0 - /* According to johnp, we don't need to bother with any of this. */ - /* Request all messages which we're allowed to see. */ - dbus_bus_add_match(ctx->conn, "type='method_call'", &err); - if (dbus_error_is_set(&err)) { - /* FIXME: what now? */ - dbus_error_free(&err); - } - dbus_bus_add_match(ctx->conn, "type='method_return'", &err); - if (dbus_error_is_set(&err)) { - /* FIXME: what now? */ - dbus_error_free(&err); - } - dbus_bus_add_match(ctx->conn, "type='error'", &err); - if (dbus_error_is_set(&err)) { - /* FIXME: what now? */ - dbus_error_free(&err); - } -#endif + /* Disconnect. */ + service_disconnect(srv); - /* Re-register our filter, which does the dispatching. */ - ctx->registered = dbus_connection_add_filter(ctx->conn, - oddjob_dbus_filter, - ctx, - NULL); - if (!ctx->registered) { - /* FIXME: what if this fails? */; + /* Reconnect. */ + service_connect(srv); } - - /* Attempt to rebind to the service names we had. */ - for (i = 0; i < ctx->n_services; i++) { - if (!oddjob_dbus_bind(ctx->conn, ctx->services[i].name)) { - /* FIXME: what if one of these returns FALSE? */; - } - } - - /* Get ready to process more messages. */ - mainloop_reinit(ctx->conn); } -static struct oddjob_dbus_service * -guess_service(struct oddjob_dbus_context *ctx, const char *path, - const char *interface, const char *method) +/* Check if a message matches a given path. */ +static dbus_bool_t +oddjob_dbus_message_has_path(DBusMessage *message, const char *path) { - int i, j, k, l; - struct oddjob_dbus_service *srv, *matched = NULL; - struct oddjob_dbus_object *obj; - struct oddjob_dbus_interface *iface; - - for (i = 0; i < ctx->n_services; i++) { - srv = &ctx->services[i]; - for (j = 0; j < srv->n_objects; j++) { - if (fnmatch(srv->objects[j].path, path, - ODDJOB_OBJECT_FNMATCH_FLAGS) != 0) { - continue; - } - obj = &srv->objects[j]; - for (k = 0; k < obj->n_interfaces; k++) { - if ((interface != NULL) && - (strcmp(interface, obj->interfaces[k].interface) != 0)) { - continue; - } - iface = &obj->interfaces[k]; - for (l = 0; l < iface->n_methods; l++) { - if (strcmp(method, iface->methods[l].method) != 0) { - continue; - } - if ((matched != NULL) && (matched != srv)) { - return NULL; - } - matched = srv; - } - } - } +#if DBUS_CHECK_VERSION(0,34,0) + return dbus_message_has_path(message, path); +#elif DBUS_CHECK_VERSION(0,20,0) + const char *msgpath; + msgpath = dbus_message_get_path(message); + if ((msgpath == NULL) && (path == NULL)) { + return TRUE; + } + if ((msgpath != NULL) && (path != NULL) && + (strcmp(msgpath, path) == 0)) { + return TRUE; } - return matched; + return FALSE; +#else +#error "Don't know how to check message information in your version of D-Bus!" +#endif } +/* Find exactly one interface of this object which provides the listed method, + * for cases where the interface name was omitted from the call message. */ static struct oddjob_dbus_interface * guess_interface(struct oddjob_dbus_object *obj, const char *method) { @@ -655,6 +627,7 @@ guess_interface(struct oddjob_dbus_object *obj, const char *method) return match; } +/* Handle incoming messages. */ static DBusHandlerResult oddjob_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data) { @@ -672,14 +645,26 @@ oddjob_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data) struct passwd *pwd; int i, j; + /* Figure out which connection received the request. We'll use its + * name instead of the destination from the message, in case it was + * addressed to the unique name instead of the well-known name. */ ctx = user_data; + for (i = 0; i < ctx->n_services; i++) { + srv = &ctx->services[i]; + if (srv->conn == conn) { + break; + } + } + if (i > ctx->n_services) { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } /* If it's a global signal, check for disconnect. */ if (ctx->reconnect_timeout > 0) { /* Disconnect from the message bus itself. */ if (dbus_message_has_sender(message, DBUS_SERVICE_DBUS) && - message_has_path(message, DBUS_PATH_DBUS) && + oddjob_dbus_message_has_path(message, DBUS_PATH_DBUS) && dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "Disconnected")) { @@ -687,15 +672,15 @@ oddjob_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data) } /* Disconnect from the library. */ #if DBUS_CHECK_VERSION(0,30,0) - if (message_has_path(message, DBUS_PATH_LOCAL) && + if (oddjob_dbus_message_has_path(message, DBUS_PATH_LOCAL) && dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } #elif DBUS_CHECK_VERSION(0,20,0) - if (message_has_path(message, - DBUS_PATH_ORG_FREEDESKTOP_LOCAL) && + if (oddjob_dbus_message_has_path(message, + DBUS_PATH_ORG_FREEDESKTOP_LOCAL) && dbus_message_is_signal(message, DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL, "Disconnected")) { @@ -707,7 +692,7 @@ oddjob_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data) } /* We only care about method calls to our services, so check that it's a - * method call to one of our well-known names. */ + * method call that we know how to handle. */ called_service = dbus_message_get_destination(message); called_path = dbus_message_get_path(message); called_interface = dbus_message_get_interface(message); @@ -726,7 +711,7 @@ oddjob_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data) } } - /* Build our return message structure. */ + /* Build our message structure. */ msg = oddjob_dbus_message_from_message(conn, message, FALSE, TRUE); if (msg == NULL) { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -742,23 +727,6 @@ oddjob_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data) return DBUS_HANDLER_RESULT_HANDLED; } - /* Get the called service name and find the service. */ - for (i = 0; i < ctx->n_services; i++) { - if (strcmp(ctx->services[i].name, called_service) == 0) { - break; - } - } - if (i >= ctx->n_services) { - srv = guess_service(ctx, called_path, called_interface, - called_member); - if (srv == NULL) { - oddjob_dbus_message_free(msg); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } else { - srv = &ctx->services[i]; - } - /* Get the called object path and find the object. */ for (i = 0; (called_path != NULL) && (i < srv->n_objects); i++) { if (fnmatch(srv->objects[i].path, called_path, @@ -880,20 +848,20 @@ oddjob_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data) } dbus_bool_t -oddjob_dbus_listener_add_method(struct oddjob_dbus_context *ctx, - const char *service_name, - const char *object_path, - const char *interface, - const char *method, - int n_arguments, - oddjob_dbus_handler *handler, - void *data) +oddjob_dbus_listeners_add_method(struct oddjob_dbus_context *ctx, + const char *service_name, + const char *object_path, + const char *interface, + const char *method, + int n_arguments, + oddjob_dbus_handler *handler, + void *data) { - int i; struct oddjob_dbus_service *srv; struct oddjob_dbus_object *obj; struct oddjob_dbus_interface *interf; struct oddjob_dbus_method *meth; + int i; /* find the service, creating it if it doesn't already exist. */ for (i = 0; i < ctx->n_services; i++) { @@ -903,23 +871,20 @@ oddjob_dbus_listener_add_method(struct oddjob_dbus_context *ctx, } } if (i >= ctx->n_services) { - /* Try to set ourselves up with the specified well-known - * service name. If it fails, there may already be another - * copy running, but that's not our problem to solve. */ - if (!oddjob_dbus_bind(ctx->conn, service_name)) { - fprintf(stderr, "Error binding to service name " - "\"%s\"!\n", service_name); - return FALSE; - } oddjob_resize_array((void**)&ctx->services, sizeof(ctx->services[0]), ctx->n_services, i + 1); - ctx->services[i].name = oddjob_strdup(service_name); - ctx->services[i].objects = NULL; - ctx->services[i].n_objects = 0; + srv = &ctx->services[i]; + memset(srv, 0, sizeof(*srv)); + srv->name = oddjob_strdup(service_name); + srv->ctx = ctx; + if (!service_connect(srv)) { + return FALSE; + } ctx->n_services = i + 1; + } else { + srv = &ctx->services[i]; } - srv = &ctx->services[i]; /* find the object, creating it if it doesn't already exist. */ for (i = 0; i < srv->n_objects; i++) { @@ -976,24 +941,15 @@ oddjob_dbus_listener_add_method(struct oddjob_dbus_context *ctx, meth->handler = handler; meth->data = data; - /* last step - if we haven't added a filter yet, do that */ - if (!ctx->registered) { - ctx->registered = dbus_connection_add_filter(ctx->conn, - oddjob_dbus_filter, - ctx, - NULL); - - } - return TRUE; } dbus_bool_t -oddjob_dbus_listener_remove_method(struct oddjob_dbus_context *ctx, - const char *service_name, - const char *object_path, - const char *interface, - const char *method) +oddjob_dbus_listeners_remove_method(struct oddjob_dbus_context *ctx, + const char *service_name, + const char *object_path, + const char *interface, + const char *method) { int i; struct oddjob_dbus_service *srv; @@ -1143,10 +1099,11 @@ oddjob_dbus_listener_remove_method(struct oddjob_dbus_context *ctx, return TRUE; } - /* now, unbind from the service. if the listener has exactly one + /* now, stop offering the service. if the listener has exactly one * service, free it, else just remove this service from its list */ - oddjob_dbus_unbind(ctx->conn, srv->name); + service_disconnect(srv); oddjob_free(srv->name); + if (ctx->n_services > 1) { for (i = 0; i < ctx->n_services; i++) { if (&ctx->services[i] == srv) { @@ -1168,19 +1125,6 @@ oddjob_dbus_listener_remove_method(struct oddjob_dbus_context *ctx, ctx->n_services = 0; } - /* if this listener still has services, we're done */ - if (ctx->n_services > 0) { - return TRUE; - } - - /* last step - if we have no services, remove the filter */ - if (ctx->registered) { - dbus_connection_remove_filter(ctx->conn, - oddjob_dbus_filter, - ctx); - ctx->registered = FALSE; - } - return TRUE; } @@ -1393,8 +1337,8 @@ oddjob_dbus_send_message_response_success(struct oddjob_dbus_message *msg, void oddjob_dbus_send_message_response_error(struct oddjob_dbus_message *msg, - const char *error, - const char *text) + const char *error, + const char *text) { DBusMessage *message; message = dbus_message_new_error(msg->msg, error, text); @@ -1545,31 +1489,41 @@ oddjob_dbus_call_method(DBusBusType bus, i = oddjob_dbus_call_bus_methodv(bus, service, object_path, interface, method, - result, timeout_milliseconds, - output, output_length, - error, error_length, - argv); + result, timeout_milliseconds, + output, output_length, + error, error_length, + argv); oddjob_free(argv); return i; } -void -oddjob_dbus_main_init(struct oddjob_dbus_context *ctx) -{ - mainloop_reset_signal_handlers(); - mainloop_init(ctx->conn); -} - -void -oddjob_dbus_main_done(struct oddjob_dbus_context *ctx) -{ - mainloop_done(ctx->conn); -} - int oddjob_dbus_main_iterate(struct oddjob_dbus_context *ctx) { - return mainloop_iterate(ctx->conn); + struct oddjob_dbus_service *srv; + int ret = 0, i; + + mainloop_reset_signal_handlers(); + ret = mainloop_iterate(); + for (i = 0; i < ctx->n_services; i++) { + srv = &ctx->services[i]; + while (dbus_connection_get_dispatch_status(srv->conn) == + DBUS_DISPATCH_DATA_REMAINS) { + dbus_connection_dispatch(srv->conn); + } +#if DBUS_CHECK_VERSION(0,30,0) + while (dbus_connection_has_messages_to_send(srv->conn)) { + dbus_connection_flush(srv->conn); + } +#elif DBUS_CHECK_VERSION(0,20,0) + while (dbus_connection_get_outgoing_size(srv->conn) > 0) { + dbus_connection_flush(srv->conn); + } +#else +#error "Don't know how to check if messages need to be flushed!" +#endif + } + return ret; } const char * diff --git a/src/oddjob_dbus.h b/src/oddjob_dbus.h index 94c2ce0..376121d 100644 --- a/src/oddjob_dbus.h +++ b/src/oddjob_dbus.h @@ -1,5 +1,5 @@ /* - Copyright 2005,2012 Red Hat, Inc. + Copyright 2005,2012,2015 Red Hat, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -39,11 +39,11 @@ struct oddjob_dbus_context; struct oddjob_dbus_message; /* Server */ -struct oddjob_dbus_context *oddjob_dbus_listener_new(DBusBusType bus); -void oddjob_dbus_listener_reconnect_if_needed(struct oddjob_dbus_context *ctx); -void oddjob_dbus_listener_set_reconnect_timeout(struct oddjob_dbus_context *ctx, - int timeout); -void oddjob_dbus_listener_free(struct oddjob_dbus_context *ctx); +struct oddjob_dbus_context *oddjob_dbus_listeners_new(DBusBusType bus); +void oddjob_dbus_listeners_reconnect_if_needed(struct oddjob_dbus_context *ctx); +void oddjob_dbus_listeners_set_reconnect_timeout(struct oddjob_dbus_context *ctx, + int timeout); +void oddjob_dbus_listeners_free(struct oddjob_dbus_context *ctx); typedef void (oddjob_dbus_handler)(struct oddjob_dbus_context *ctx, struct oddjob_dbus_message *msg, const char *service_name, @@ -53,20 +53,20 @@ typedef void (oddjob_dbus_handler)(struct oddjob_dbus_context *ctx, const char *user, unsigned long uid, void *data); -dbus_bool_t oddjob_dbus_listener_add_method(struct oddjob_dbus_context *ctx, - const char *service_name, - const char *object_path, - const char *interface, - const char *method, - int n_arguments, - oddjob_dbus_handler *handler, - void *data); -dbus_bool_t oddjob_dbus_listener_remove_method(struct oddjob_dbus_context *ctx, - const char *service_name, - const char *object_path, - const char *interface, - const char *method); - +dbus_bool_t oddjob_dbus_listeners_add_method(struct oddjob_dbus_context *ctx, + const char *service_name, + const char *object_path, + const char *interface, + const char *method, + int n_arguments, + oddjob_dbus_handler *handler, + void *data); +dbus_bool_t oddjob_dbus_listeners_remove_method(struct oddjob_dbus_context *ctx, + const char *service_name, + const char *object_path, + const char *interface, + const char *method); + int oddjob_dbus_message_get_n_args(struct oddjob_dbus_message *msg); const char *oddjob_dbus_message_get_arg(struct oddjob_dbus_message *msg, int n); const char *oddjob_dbus_message_get_selinux_context(struct oddjob_dbus_message *msg); @@ -87,8 +87,6 @@ void oddjob_dbus_send_message_response_success(struct oddjob_dbus_message *msg, void oddjob_dbus_send_message_response_error(struct oddjob_dbus_message *msg, const char *error, const char *text); -void oddjob_dbus_main_init(struct oddjob_dbus_context *ctx); -void oddjob_dbus_main_done(struct oddjob_dbus_context *ctx); int oddjob_dbus_main_iterate(struct oddjob_dbus_context *ctx); /* Clients */ diff --git a/src/oddjobd.c b/src/oddjobd.c index d8c9159..d0a9085 100644 --- a/src/oddjobd.c +++ b/src/oddjobd.c @@ -2730,14 +2730,14 @@ config_register(struct oddjob_dbus_context *ctx, struct oddjob_config *config, /* If the handler used NULL, give it * the method structure address, which * wasn't fixed until now. */ - reg = oddjob_dbus_listener_add_method(ctx, - service->name, - object->name, - interface->name, - method->name, - method->n_arguments, - method->handler, - method); + reg = oddjob_dbus_listeners_add_method(ctx, + service->name, + object->name, + interface->name, + method->name, + method->n_arguments, + method->handler, + method); if (!reg) { fprintf(stderr, "Error initializing service \"%s\"!\n", service->name); @@ -2788,11 +2788,11 @@ config_unregister_removed(struct oddjob_dbus_context *ctx, interface->name, method->name); } - oddjob_dbus_listener_remove_method(ctx, - service->name, - object->name, - interface->name, - method->name); + oddjob_dbus_listeners_remove_method(ctx, + service->name, + object->name, + interface->name, + method->name); } } } @@ -2984,13 +2984,13 @@ main(int argc, char **argv) globals.config = config; /* Open a connection to the message bus. */ - ctx = oddjob_dbus_listener_new(options.bus); + ctx = oddjob_dbus_listeners_new(options.bus); if (ctx == NULL) { fprintf(stderr, "Error connecting to D-Bus!\n"); return 2; } - oddjob_dbus_listener_set_reconnect_timeout(ctx, - options.reconnect_timeout); + oddjob_dbus_listeners_set_reconnect_timeout(ctx, + options.reconnect_timeout); /* Add introspection methods to every object. */ config_add_introspection_methods(config); @@ -3066,21 +3066,19 @@ main(int argc, char **argv) } /* Now sit! */ - oddjob_dbus_main_init(ctx); globals.quit = 0; while (globals.quit == 0) { if (globals.reload) { oddjobd_reload_configuration(ctx, NULL); globals.reload = 0; } - oddjob_dbus_listener_reconnect_if_needed(ctx); + oddjob_dbus_listeners_reconnect_if_needed(ctx); oddjob_dbus_main_iterate(ctx); } - oddjob_dbus_main_done(ctx); /* Clean up and exit. */ unload_config(globals.config); - oddjob_dbus_listener_free(ctx); + oddjob_dbus_listeners_free(ctx); if (options.pidfile != NULL) { unlink(options.pidfile); }