DBusWatch and DBusTimeout examples

后端 未结 3 1747
孤街浪徒
孤街浪徒 2020-12-13 21:59

I need to write an application in C for asynchronous sending and reading messages on the dbus message queue. I\'ve read that for doing that I should use the DBusWatch<

3条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-13 22:41

    Here's something I wrote some time ago. I removed application specific code, you should just add your snippets where you handle DBus messages meant for your application and that should be it.

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    
    struct dbus_ctx {
        DBusConnection *conn;
        struct event_base *evbase;
        struct event dispatch_ev;
        void *extra;
    };
    
    static void dispatch(int fd, short ev, void *x)
    {
        struct dbus_ctx *ctx = x;
        DBusConnection *c = ctx->conn;
    
        logger(LOG_DEBUG "dispatching\n");
    
        while (dbus_connection_get_dispatch_status(c) == DBUS_DISPATCH_DATA_REMAINS)
            dbus_connection_dispatch(c);
    }
    
    static void handle_dispatch_status(DBusConnection *c,
                                       DBusDispatchStatus status, void *data)
    {
        struct dbus_ctx *ctx = data;
    
        logger(LOG_DEBUG "new dbus dispatch status: %d\n", status);
    
        if (status == DBUS_DISPATCH_DATA_REMAINS) {
            struct timeval tv = {
                .tv_sec = 0,
                .tv_usec = 0,
            };
            event_add(&ctx->dispatch_ev, &tv);
        }
    }
    
    static void handle_watch(int fd, short events, void *x)
    {
        struct dbus_ctx *ctx = x;
        struct DBusWatch *watch = ctx->extra;
    
        unsigned int flags = 0;
        if (events & EV_READ)
            flags |= DBUS_WATCH_READABLE;
        if (events & EV_WRITE)
            flags |= DBUS_WATCH_WRITABLE;
        /*if (events & HUP)
            flags |= DBUS_WATCH_HANGUP;
        if (events & ERR)
            flags |= DBUS_WATCH_ERROR;*/
    
        logger(LOG_DEBUG "got dbus watch event fd=%d watch=%p ev=%d\n",
               fd, watch, events);
        if (dbus_watch_handle(watch, flags) == FALSE)
            logger(LOG_ERROR "dbus_watch_handle() failed\n");
    
        handle_dispatch_status(ctx->conn, DBUS_DISPATCH_DATA_REMAINS, ctx);
    }
    
    static dbus_bool_t add_watch(DBusWatch *w, void *data)
    {
        if (!dbus_watch_get_enabled(w))
            return TRUE;
    
        struct dbus_ctx *ctx = data;
        ctx->extra = w;
    
        int fd = dbus_watch_get_unix_fd(w);
        unsigned int flags = dbus_watch_get_flags(w);
        short cond = EV_PERSIST;
        if (flags & DBUS_WATCH_READABLE)
            cond |= EV_READ;
        if (flags & DBUS_WATCH_WRITABLE)
            cond |= EV_WRITE;
    
        struct event *event = event_new(ctx->evbase, fd, cond, handle_watch, ctx);
        if (!event)
            return FALSE;
    
        event_add(event, NULL);
    
        dbus_watch_set_data(w, event, NULL);
    
        logger(LOG_DEBUG "added dbus watch fd=%d watch=%p cond=%d\n", fd, w, cond);
        return TRUE;
    }
    
    static void remove_watch(DBusWatch *w, void *data)
    {
        struct event *event = dbus_watch_get_data(w);
    
        if (event)
            event_free(event);
    
        dbus_watch_set_data(w, NULL, NULL);
    
        logger(LOG_DEBUG "removed dbus watch watch=%p\n", w);
    }
    
    static void toggle_watch(DBusWatch *w, void *data)
    {
        logger(LOG_DEBUG "toggling dbus watch watch=%p\n", w);
    
        if (dbus_watch_get_enabled(w))
            add_watch(w, data);
        else
            remove_watch(w, data);
    }
    
    static void handle_timeout(int fd, short ev, void *x)
    {
        struct dbus_ctx *ctx = x;
        DBusTimeout *t = ctx->extra;
    
        logger(LOG_DEBUG "got dbus handle timeout event %p\n", t);
    
        dbus_timeout_handle(t);
    }
    
    static dbus_bool_t add_timeout(DBusTimeout *t, void *data)
    {
        struct dbus_ctx *ctx = data;
    
        if (!dbus_timeout_get_enabled(t))
            return TRUE;
    
        logger(LOG_DEBUG "adding timeout %p\n", t);
    
        struct event *event = event_new(ctx->evbase, -1, EV_TIMEOUT|EV_PERSIST,
                                        handle_timeout, t);
        if (!event) {
            logger(LOG_ERROR "failed to allocate new event for timeout\n");
            return FALSE;
        }
    
        int ms = dbus_timeout_get_interval(t);
        struct timeval tv = {
            .tv_sec = ms / 1000,
            .tv_usec = (ms % 1000) * 1000,
        };
        event_add(event, &tv);
    
        dbus_timeout_set_data(t, event, NULL);
    
        return TRUE;
    }
    
    static void remove_timeout(DBusTimeout *t, void *data)
    {
        struct event *event = dbus_timeout_get_data(t);
    
        logger(LOG_DEBUG "removing timeout %p\n", t);
    
        event_free(event);
    
        dbus_timeout_set_data(t, NULL, NULL);
    }
    
    static void toggle_timeout(DBusTimeout *t, void *data)
    {
        logger(LOG_DEBUG "toggling timeout %p\n", t);
    
        if (dbus_timeout_get_enabled(t))
            add_timeout(t, data);
        else
            remove_timeout(t, data);
    }
    
    static DBusHandlerResult handle_nameownerchanged(DBusMessage *message,
                                                     void *data)
    {
        struct dbus_ctx *ctx = data;
        char *name, *old, *new;
        if (dbus_message_get_args(message, NULL,
                                  DBUS_TYPE_STRING, &name,
                                  DBUS_TYPE_STRING, &old,
                                  DBUS_TYPE_STRING, &new,
                                  DBUS_TYPE_INVALID) == FALSE) {
            logger(LOG_ERROR "spurious NameOwnerChanged signal\n");
            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
        }
        logger(LOG_DEBUG "dbus NameOwnerChanged %s -> %s\n", old, new);
    
        if (new[0] != '\0')
            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    
        /* XXX handle disconnecting clients */
    
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }
    
    static DBusHandlerResult msg_filter(DBusConnection *connection,
                                        DBusMessage *message, void *data)
    {
        if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
                                   "NameOwnerChanged"))
            return handle_nameownerchanged(message, data);
    
        logger(LOG_DEBUG "got dbus message %d %s -> %s %s/%s/%s %s\n",
               dbus_message_get_type(message),
               dbus_message_get_sender(message),
               dbus_message_get_destination(message),
               dbus_message_get_path(message),
               dbus_message_get_interface(message),
               dbus_message_get_member(message),
               dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR ?
               dbus_message_get_error_name(message) : "");
    
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }
    
    static void unregister_func(DBusConnection *connection, void *data)
    {
    }
    
    static DBusHandlerResult message_func(DBusConnection *connection,
                                          DBusMessage *message, void *data)
    {
        struct dbus_ctx *ctx = data;
    
        logger(LOG_DEBUG "got dbus message sent to %s %s %s\n",
               dbus_message_get_destination(message),
               dbus_message_get_interface(message),
               dbus_message_get_path(message));
    
        /* XXX handle DBus message */
    
        return DBUS_HANDLER_RESULT_HANDLED;
    }
    
    static DBusObjectPathVTable dbus_vtable = {
        .unregister_function = unregister_func,
        .message_function = message_func,
    };
    
    struct dbus_ctx *dbus_init(struct event_base *eb)
    {
        DBusConnection *conn = NULL;
        struct dbus_ctx *ctx = calloc(1, sizeof(struct dbus_ctx));
        if (!ctx) {
            logger_perror("can't allocate dbus_ctx\n");
            goto out;
        }
    
        conn = dbus_bus_get_private(DBUS_BUS_SESSION, NULL);
        if (conn == NULL) {
            logger(LOG_ERROR "failed to get bus\n");
            goto out;
        }
    
        dbus_connection_set_exit_on_disconnect(conn, FALSE);
    
        ctx->conn = conn;
        ctx->evbase = eb;
        event_assign(&ctx->dispatch_ev, eb, -1, EV_TIMEOUT, dispatch, ctx);
    
        if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
                                                 toggle_watch, ctx, NULL)) {
            logger(LOG_ERROR "dbus_connection_set_watch_functions() failed\n");
            goto out;
        }
    
        if (!dbus_connection_set_timeout_functions(conn, add_timeout,
                                                   remove_timeout, toggle_timeout,
                                                   ctx, NULL)) {
            logger(LOG_ERROR "dbus_connection_set_timeout_functions() failed\n");
            goto out;
        }
    
        if (dbus_connection_add_filter(conn, msg_filter, ctx, NULL) == FALSE) {
            logger(LOG_ERROR "dbus_connection_add_filter() failed\n");
            goto out;
        }
    
        dbus_connection_set_dispatch_status_function(conn, handle_dispatch_status,
                                                     ctx, NULL);
    
        char match[256];
        snprintf(match,
                 sizeof(match),
                 "type='signal',interface='%s',member='NameOwnerChanged'",
                 DBUS_INTERFACE_DBUS);
        DBusError error;
        dbus_error_init(&error);
        dbus_bus_add_match(conn, match, &error);
        if (dbus_error_is_set(&error)) {
            logger(LOG_ERROR "dbus_bus_add_match() %s failed: %s\n",
                   "NameOwnerChanged", error.message);
            dbus_error_free(&error);
            goto out;
        }
    
        snprintf(match,
                 sizeof(match),
                 "type='signal',interface='%s',member='%s'",
                 GNP_IPC_INTERFACE, GNP_IPC_SIGNAL_DELIVER_SA);
        dbus_error_init(&error);
        dbus_bus_add_match(conn, match, &error);
        if (dbus_error_is_set(&error)) {
            logger(LOG_ERROR "dbus_bus_add_match() %s failed: %s\n",
                   GNP_IPC_SIGNAL_DELIVER_SA, error.message);
            dbus_error_free(&error);
            goto out;
        }
    
        if (dbus_connection_register_object_path(conn, GNP_IPC_PATH, &dbus_vtable,
                                                 ctx) != TRUE) {
            logger(LOG_ERROR "failed to register object path\n");
            goto out;
        }
    
        return ctx;
    
    out:
        if (conn) {
            dbus_connection_close(conn);
            dbus_connection_unref(conn);
        }
        if (ctx)
            free(ctx);
        return NULL;
    }
    
    void dbus_close(struct dbus_ctx *ctx)
    {
        if (ctx && ctx->conn) {
            dbus_connection_flush(ctx->conn);
            dbus_connection_close(ctx->conn);
            dbus_connection_unref(ctx->conn);
            event_del(&ctx->dispatch_ev);
        }
        if (ctx)
            free(ctx);
    }
    

提交回复
热议问题