From 8930d787dfc20e1514d6f03e41690df0e9a507da Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 26 Oct 2018 15:25:36 +0200 Subject: [PATCH 01/17] Add subd headers and sources These provide a small set of wrapper functions around the low-level libdbus API that act like a shim for sd-bus. --- dbus/subd-sdbus.c | 403 +++++++++++++++++++++++++++++++++++++++++++ dbus/subd-vtable.c | 337 ++++++++++++++++++++++++++++++++++++ dbus/subd-watch.c | 180 +++++++++++++++++++ include/subd-sdbus.h | 142 +++++++++++++++ include/subd.h | 41 +++++ 5 files changed, 1103 insertions(+) create mode 100644 dbus/subd-sdbus.c create mode 100644 dbus/subd-vtable.c create mode 100644 dbus/subd-watch.c create mode 100644 include/subd-sdbus.h create mode 100644 include/subd.h diff --git a/dbus/subd-sdbus.c b/dbus/subd-sdbus.c new file mode 100644 index 0000000..eccdc14 --- /dev/null +++ b/dbus/subd-sdbus.c @@ -0,0 +1,403 @@ +#include +#include +#include +#include "subd.h" + +int sd_bus_open_user(sd_bus **bus) { + if (!dbus_threads_init_default()) { + return -ENOMEM; + } + + *bus = dbus_bus_get(DBUS_BUS_SESSION, NULL); + if (*bus == NULL) { + return -ENXIO; + } + + return 0; +} + +int sd_bus_request_name(sd_bus *bus, const char *name, int flags) { + if (dbus_bus_request_name(bus, name, flags, NULL) == -1) { + return -EEXIST; + } + + return 0; +} + +void sd_bus_slot_unref(int *slot) { + // not used +} + +void sd_bus_flush_close_unref(sd_bus *bus) { + // only flush and unref, no need to dbus_connection_close shared connections + dbus_connection_flush(bus); + dbus_connection_unref(bus); +} + +void sd_bus_error_set_const(sd_bus_error *err, const char *name, const char *msg) { + dbus_set_error_const(err, name, msg); +} + +int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newmsg) { + sd_bus_message *new_message = NULL; + DBusMessage *message = NULL; + // Allocate a new "method return" DBusMessage + if ((message = dbus_message_new_method_return(msg->message)) == NULL) { + goto error; + } + + // Allocate a new sd_bus_message + new_message = malloc(sizeof(sd_bus_message)); + if (new_message == NULL) { + goto error; + } + + // Populate the fields of the new sd_bus_message so that it knows which bus + // it is to be sent on, and what DBusMessage it contains. + new_message->bus = msg->bus; + new_message->message = message; + new_message->ref_count = 1; + + //Also initialize the sd_bus_message's iterator stack, ... + new_message->iters = malloc(sizeof(struct wl_list)); + if (new_message->iters == NULL) { + goto error; + } + wl_list_init(new_message->iters); + + // ... and push a new append iterator to it. + struct msg_iter *iter = malloc(sizeof(struct msg_iter)); + if (iter == NULL) { + goto error; + } + dbus_message_iter_init_append(new_message->message, &iter->iter); + wl_list_insert(new_message->iters, &iter->link); + new_message->iter = &iter->iter; + + *newmsg = new_message; + return 0; + +error: + //XXX list_destroy(new_message->iters); + free(new_message); + dbus_message_unref(message); + *newmsg = NULL; + return -ENOMEM; +} + +int sd_bus_message_open_container(sd_bus_message *msg, char type, + const char *signature) { + struct msg_iter *sub = malloc(sizeof(struct msg_iter)); + if (sub == NULL) { + goto error; + } + + if (!dbus_message_iter_open_container(msg->iter, type, signature, &sub->iter)) { + goto error; + } + wl_list_insert(msg->iters, &sub->link); + msg->iter = &sub->iter; + + return 0; + +error: + free(sub); + return -ENOMEM; +} + +int sd_bus_message_close_container(sd_bus_message *msg) { + // Remove the iterator we want to close from from the iterator stack + struct msg_iter *iter = wl_container_of(msg->iters->next, iter, link); + wl_list_remove(&iter->link); + + // Set msg's current iterator to the top of the iterator stack. + struct msg_iter *new_head = wl_container_of(msg->iters->next, iter, link); + msg->iter = &new_head->iter; + + // Close the container. + if (!dbus_message_iter_close_container(msg->iter, &iter->iter)) { + return -ENOMEM; + } + + return 0; +} + +int sd_bus_message_append(sd_bus_message *msg, const char *signature, ...) { + if (!dbus_signature_validate(signature, NULL)) { + return -EINVAL; + } + + va_list ap; + DBusSignatureIter iter; + va_start(ap, signature); + dbus_signature_iter_init(&iter, signature); + + do { + int type = dbus_signature_iter_get_current_type(&iter); + if (!dbus_type_is_basic(type)) { + va_end(ap); + return -EINVAL; + } + + DBusBasicValue val = va_arg(ap, DBusBasicValue); + if (!dbus_message_iter_append_basic(msg->iter, type, &val)) { + va_end(ap); + return -ENOMEM; + } + } while(dbus_signature_iter_next(&iter)); + + va_end(ap); + return 0; +} + +void sd_bus_message_unref(sd_bus_message *msg) { + dbus_message_unref(msg->message); + if (msg->ref_count == 0) { + //XXX list_destroy(msg->iters); + free(msg); + } +} + +int sd_bus_message_peek_type(sd_bus_message *msg, void *unused, + const char **signature) { + char *s = dbus_message_iter_get_signature(msg->iter); + if (s == NULL) { + return -ENOMEM; + } + + *signature = strdup(s); + dbus_free(s); + return 0; +} + +int sd_bus_message_readv(sd_bus_message *msg, const char *signature, va_list ap) { + // Immediately return 0 if there are not arguments to read. + if (dbus_message_iter_get_arg_type(msg->iter) == DBUS_TYPE_INVALID) { + return 0; + } + + DBusSignatureIter iter; + dbus_signature_iter_init(&iter, signature); + do { + // Bail out if the signature contains a non-basic type. + int type = dbus_signature_iter_get_current_type(&iter); + if (!dbus_type_is_basic(type)) { + va_end(ap); + return -EINVAL; + } + + // Bail out if signature does not match the actual argument. + if (dbus_message_iter_get_arg_type(msg->iter) != type) { + return -ENXIO; + } + + // Read the value. + DBusBasicValue *ptr = va_arg(ap, DBusBasicValue *); + if (ptr != NULL) { + dbus_message_iter_get_basic(msg->iter, ptr); + } + + // Don't check for "no more args" condition here, since it is possible + // that there are no more elements in the signature either. The erronous + // "signature not ended, but no more args" scenario is dealt with at the + // beginning of the loop body by DBUS_TYPE_INVALID not being a basic + // type. + dbus_message_iter_next(msg->iter); + } while (dbus_signature_iter_next(&iter)); + + return dbus_message_iter_has_next(msg->iter); +} + +int sd_bus_message_read(sd_bus_message *msg, const char *signature, ...) { + int ret = 0; + va_list ap; + va_start(ap, signature); + + // NOTE: There is a difference here between the real sd_bus_message_read and + // this wrapper. We only support messages that + // - contain a variant type that contain only basic types, + // - or contain only basic types. + // + // This is currently enough for Mako, but I left this note in case this + // function needs to be expanded in the future. + if (strcmp(signature, "v") == 0) { + const char *inner_signature = va_arg(ap, char *); + sd_bus_message_enter_container(msg, 'v', inner_signature); + ret = sd_bus_message_readv(msg, inner_signature, ap); + sd_bus_message_exit_container(msg); + } else { + ret = sd_bus_message_readv(msg, signature, ap); + } + + va_end(ap); + return ret; +} + +int sd_bus_message_skip(sd_bus_message *msg, const char *signature) { + // Immediately return 0 if there are not arguments to read. + if (dbus_message_iter_get_arg_type(msg->iter) == DBUS_TYPE_INVALID) { + return 0; + } + + DBusSignatureIter iter; + dbus_signature_iter_init(&iter, signature); + do { + // Bail out if the signature contains a non-basic type. + int type = dbus_signature_iter_get_current_type(&iter); + if (!dbus_type_is_basic(type)) { + return -EINVAL; + } + + // Bail out if signature does not match the actual argument. + if (dbus_message_iter_get_arg_type(msg->iter) != type) { + return -ENXIO; + } + + // Don't check for "no more args" condition here, since it is possible + // that there are no more elements in the signature either. The erronous + // "signature not ended, but no more args" scenario is dealt with at the + // beginning of the loop body by DBUS_TYPE_INVALID not being a basic + // type. + dbus_message_iter_next(msg->iter); + } while (dbus_signature_iter_next(&iter)); + + return dbus_message_iter_has_next(msg->iter); +} + +int sd_bus_message_enter_container(sd_bus_message *msg, char type, + const char *signature) { + // Immediately return 0 if there are no arguments to read (or in this case, + // no container to enter), or -ENXIO if signature and actual argument type + // does not match. + int t = dbus_message_iter_get_arg_type(msg->iter); + if (t == DBUS_TYPE_INVALID) { + return 0; + } else if (t != type) { + return -ENXIO; + } + + // Allocate an iterator, recurse into it, and set it as the current iterator + // of the message. + struct msg_iter *sub_iter = malloc(sizeof(struct msg_iter)); + if (sub_iter == NULL) { + return -ENOMEM; + } + + dbus_message_iter_recurse(msg->iter, &sub_iter->iter); + wl_list_insert(msg->iters, &sub_iter->link); + msg->iter = &sub_iter->iter; + + return dbus_message_iter_has_next(&sub_iter->iter); +} + +int sd_bus_message_exit_container(sd_bus_message *msg) { + // Remove the iterator we want to exit from from the iterator stack + struct msg_iter *iter = wl_container_of(msg->iters->next, iter, link); + wl_list_remove(&iter->link); + + // Set msg's current iterator to the top of the iterator stack, and use + // dbus_message_iter_next to "step over" the iterator we are exiting. + iter = wl_container_of(msg->iters->next, iter, link); + msg->iter = &iter->iter; + dbus_message_iter_next(msg->iter); + + return 0; +} + +int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, + const char *name, const char *signature, ...) { + int ret = 0; + va_list ap; + va_start(ap, signature); + + DBusMessage *signal = NULL; + if ((signal = dbus_message_new_signal(path, interface, name)) == NULL) { + ret = -ENOMEM; + goto finish; + } + + DBusSignatureIter signature_iter; + DBusMessageIter message_iter; + dbus_signature_iter_init(&signature_iter, signature); + dbus_message_iter_init_append(signal, &message_iter); + do { + int type = dbus_signature_iter_get_current_type(&signature_iter); + if (!dbus_type_is_basic(type)) { + ret = -EINVAL; + goto finish; + } + + DBusBasicValue val = va_arg(ap, DBusBasicValue); + if (!dbus_message_iter_append_basic(&message_iter, type, &val)) { + ret = -ENOMEM; + goto finish; + } + } while (dbus_signature_iter_next(&signature_iter)); + + if (!dbus_connection_send(bus, signal, NULL)) { + ret = -ENOMEM; + goto finish; + } + +finish: + va_end(ap); + dbus_message_unref(signal); + return ret; +} + +int sd_bus_reply_method_return(sd_bus_message *msg, const char *signature, ...) { + int ret = 0; + va_list ap; + va_start(ap, signature); + + DBusMessage *reply = NULL; + if ((reply = dbus_message_new_method_return(msg->message)) == NULL) { + ret = -ENOMEM; + goto finish; + } + + if (strlen(signature) == 0) { + goto send; + } + + DBusSignatureIter signature_iter; + DBusMessageIter message_iter; + dbus_signature_iter_init(&signature_iter, signature); + dbus_message_iter_init_append(reply, &message_iter); + do { + int type = dbus_signature_iter_get_current_type(&signature_iter); + if (!dbus_type_is_basic(type)) { + ret = -EINVAL; + goto finish; + } + + DBusBasicValue val = va_arg(ap, DBusBasicValue); + if (!dbus_message_iter_append_basic(&message_iter, type, &val)) { + ret = -ENOMEM; + goto finish; + } + } while (dbus_signature_iter_next(&signature_iter)); + +send: + if (!dbus_connection_send(msg->bus, reply, NULL)) { + ret = -ENOMEM; + goto finish; + } + +finish: + va_end(ap); + dbus_message_unref(reply); + return ret; +} + +int sd_bus_send(sd_bus *bus, sd_bus_message *msg, dbus_uint32_t *cookie) { + // NOTE: The original sd_bus_send takes an uint64_t * as the third parameter. + // We take a dbus_uint32_t *, because dbus_connection_send uses it. + + sd_bus *conn = (bus == NULL ? msg->bus : bus); + if (!dbus_connection_send(conn, msg->message, cookie)) { + return -ENOMEM; + } + + return 0; +} diff --git a/dbus/subd-vtable.c b/dbus/subd-vtable.c new file mode 100644 index 0000000..91b7c0e --- /dev/null +++ b/dbus/subd-vtable.c @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include "subd.h" + +struct vtable_userdata { + struct wl_list *interfaces; + void *userdata; +}; + +struct interface { + const char *name; + const struct subd_member *members; + struct wl_list link; +}; + +struct path { + const char *path; + char *introspection_data; + struct wl_list *interfaces; + struct wl_list link; +}; + +static struct wl_list *paths = NULL; + +static bool add_args(FILE *stream, const char *sig, const char *end) { + DBusSignatureIter iter; + if (!dbus_signature_validate(sig, NULL)) { + return false; + } + + if (strlen(sig) > 0) { + dbus_signature_iter_init(&iter, sig); + do { + char *s = dbus_signature_iter_get_signature(&iter); + fprintf(stream, " \n", s, end); + dbus_free(s); + } while (dbus_signature_iter_next(&iter)); + } + + return true; +} + +static const char *generate_introspection_data(struct path *path) { + free(path->introspection_data); + + size_t size; + FILE *stream = open_memstream(&path->introspection_data, &size); + if (stream == NULL) { + return NULL; + } + + // Write the DOCTYPE entity, and start the element. + fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE, stream); + fputs("\n", stream); + + // Iterate through the path's interfaces, ... + struct interface *interface; + wl_list_for_each(interface, path->interfaces, link) { + fprintf(stream, " \n", interface->name); + + const struct subd_member *member = interface->members; + while (member->type != SUBD_MEMBERS_END) { + switch (member->type) { + case SUBD_METHOD: + fprintf(stream, " \n", member->m.name); + add_args(stream, member->m.input_signature, "direction=\"in\""); + add_args(stream, member->m.output_signature, "direction=\"out\""); + fprintf(stream, " \n"); + break; + case SUBD_SIGNAL: + fprintf(stream, " \n", member->s.name); + add_args(stream, member->s.signature, ""); + fprintf(stream, " \n"); + break; + case SUBD_PROPERTY: + fprintf(stream, " p.name, member->p.signature); + switch (member->p.access) { + case SUBD_PROPERTY_READ: + fprintf(stream, "\"read\" />\n"); + break; + case SUBD_PROPERTY_WRITE: + fprintf(stream, "\"write\" />\n"); + break; + case SUBD_PROPERTY_READWRITE: + fprintf(stream, "\"readwrite\" />\n"); + break; + } + break; + case SUBD_MEMBERS_END: + // not gonna happen + break; + } + member++; + } + fprintf(stream, " \n"); + } + fprintf(stream, ""); + fclose(stream); + + return path->introspection_data; +} + +static int handle_introspect(sd_bus_message *msg, void *data, sd_bus_error *err) { + // Find the path the message was sent to, so we can access its list of + // implemented interfaces. + const char *path_name = dbus_message_get_path(msg->message); + struct path *p, *path = NULL; + wl_list_for_each(p, paths, link) { + if (strcmp(p->path, path_name) == 0) { + path = p; + break; + } + } + + if (path == NULL) { + // Something is seriously weird here. Path was not found, but then how + // was the method handler called?? + dbus_set_error(err, DBUS_ERROR_INVALID_ARGS, + "Path was not found in paths list."); + return -EINVAL; + } + + if (path->introspection_data == NULL) { + // This shouldn't really happen, introspection data is generated when + // the vtable is registered. + if (generate_introspection_data(path) == NULL) { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, + "Introspection data could not be generated."); + return -ENOMEM; + } + } + + return sd_bus_reply_method_return(msg, "s", path->introspection_data); +} + +static const struct subd_member introspectable_members[] = { + {SUBD_METHOD, .m = {"Introspect", handle_introspect, "", "s"}}, + {SUBD_MEMBERS_END, .e=0}, +}; + +/** + * Helper function for vtable_dispatch that returns wither NULL, or the method + * object member with the name "name". + */ +static const struct subd_member *find_member(const struct subd_member *members, + const char *name) { + const struct subd_member *member = members; + while (member->type != SUBD_MEMBERS_END) { + if (member->type == SUBD_METHOD && strcmp(member->m.name, name) == 0) { + return member; + } + ++member; + } + return NULL; +} + +/** + * Helper function for vtable_dispatch that calls the method object member's + * handler function, and sends an error message if necessary. + */ +static bool call_method(const struct subd_member *member, DBusConnection *conn, + DBusMessage *msg, void *userdata) { + sd_bus_message *message = NULL; + message = malloc(sizeof(sd_bus_message)); + if (message == NULL) { + return false; + } + message->bus = conn; + message->message = msg; + message->ref_count = 1; + message->iters = malloc(sizeof(struct wl_list)); + if (message->iters == NULL) { + return false; + } + wl_list_init(message->iters); + + struct msg_iter *iter = malloc(sizeof(struct msg_iter)); + if (iter == NULL) { + return false; + } + dbus_message_iter_init(message->message, &iter->iter); + wl_list_insert(message->iters, &iter->link); + message->iter = &iter->iter; + + DBusError error; + dbus_error_init(&error); + if (member->m.handler(message, userdata, &error) < 0) { + //XXX: handle error == NULL + DBusMessage *error_message = + dbus_message_new_error(msg, error.name, error.message); + dbus_connection_send(conn, error_message, 0); + dbus_error_free(&error); + return false; + } + return true; +} + +/** + * This function receives a list of interfaces implemented by the destination + * object in "userdata->interfaces", and iterates through them and their members + * to find the called method. When the method is found, its handler function is + * called with "data" set to "userdata->userdata". + */ +static DBusHandlerResult vtable_dispatch(DBusConnection *connection, + DBusMessage *message, void *userdata) { + struct vtable_userdata *data = userdata; + const char *interface_name = dbus_message_get_interface(message); + const char *member_name = dbus_message_get_member(message); + if (interface_name == NULL || member_name == NULL) { + // something is wrong, no need to try with other handlers + return DBUS_HANDLER_RESULT_HANDLED; + } + + struct interface *interface; + wl_list_for_each(interface, data->interfaces, link) { + if (strcmp(interface->name, interface_name) == 0) { + const struct subd_member *member = + find_member(interface->members, member_name); + if (member != NULL) { + call_method(member, connection, message, data->userdata); + return DBUS_HANDLER_RESULT_HANDLED; + } + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const DBusObjectPathVTable dbus_vtable = { + .message_function = vtable_dispatch, + .unregister_function = NULL, +}; + +int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, + const char *path_name, const char *interface, + const sd_bus_vtable *vtable, void *userdata) { + int ret = 0; + + if (paths == NULL) { + paths = malloc(sizeof(struct path)); + if (paths == NULL) { + return -ENOMEM; + } + wl_list_init(paths); + } + + // See if this path is already registered. If it is, it must have at least + // one interface, so get a pointer to the interfaces list. + struct wl_list *interfaces = NULL; + struct path *p, *path = NULL; + wl_list_for_each(p, paths, link) { + if (strcmp(path_name, p->path) == 0) { + path = p; + interfaces = p->interfaces; + break; + } + } + + if (interfaces == NULL) { + // Path was not registered before, so first we create an interface list, + // and append Introspectable to it. We want every path to implement + // org.freedesktop.DBus.Introspectable. + // TODO: Also implement the following: + // - org.freedesktop.DBus.Peer + // - org.freedesktop.DBus.Properties + interfaces = malloc(sizeof(struct wl_list)); + if (interfaces == NULL) { + ret = -ENOMEM; + goto finish; + } + wl_list_init(interfaces); + struct interface *new_interface = malloc(sizeof(struct interface)); + if (interface == NULL) { + ret = -ENOMEM; + goto finish; + } + new_interface->name = strdup("org.freedesktop.DBus.Introspectable"); + new_interface->members = introspectable_members; + wl_list_insert(interfaces, &new_interface->link); + + // Create the path with the new interface list + struct path *new_path = malloc(sizeof(struct path)); + if (new_path == NULL) { + ret = -ENOMEM; + goto finish; + } + new_path->path = strdup(path_name); + new_path->interfaces = interfaces; + new_path->introspection_data = NULL; // This will be set later. + path = new_path; + + // ... and also register it. + // We merge userdata with the interfaces list in a struct + // vtable_userdata here, so vtable_dispatch will know what interfaces to + // traverse, and what userdata to pass to the actual handler functions. + struct vtable_userdata *data = malloc(sizeof(struct vtable_userdata)); + if (data == NULL) { + free(new_path); + ret = -ENOMEM; + goto finish; + } + data->interfaces = interfaces; + data->userdata = userdata; + if (!dbus_connection_try_register_object_path(bus, path_name, + &dbus_vtable, data, NULL)) { + free(new_path); + ret = -ENXIO; + goto finish; + } + } + + //TODO: Decide what to do if interface is already registered. Replace + //memeber list? Append new members to list? Throw an error? + + // Append the new interface to the path's interface list. + struct interface *new_interface = malloc(sizeof(struct interface)); + if (new_interface == NULL) { + ret = -ENOMEM; + goto finish; + } + new_interface->name = strdup(interface); + new_interface->members = vtable; + wl_list_insert(interfaces, &new_interface->link); + + // Append the path to the list of paths + wl_list_insert(paths, &path->link); + + // (Re)generate introspection XML for this path. + generate_introspection_data(path); + +finish: + return ret; +} diff --git a/dbus/subd-watch.c b/dbus/subd-watch.c new file mode 100644 index 0000000..ef913e7 --- /dev/null +++ b/dbus/subd-watch.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include "subd.h" + +static dbus_bool_t add_watch(DBusWatch *watch, void *data) { + struct subd_watches *watches = data; + sem_wait(&watches->mutex); + + if (!dbus_watch_get_enabled(watch)) { + sem_post(&watches->mutex); + return TRUE; + } + + if (watches->length == watches->capacity) { + int c = watches->capacity + 10; + void *t1 = realloc(watches->fds, sizeof(struct pollfd) * c); + void *t2 = realloc(watches->watches, sizeof(DBusWatch *) * c); + if (t1 == NULL || t2 == NULL) { + sem_post(&watches->mutex); + return FALSE; + } else { + watches->capacity = c; + } + } + + short mask = 0; + unsigned int flags = dbus_watch_get_flags(watch); + if (flags & DBUS_WATCH_READABLE) { + mask |= POLLIN; + } + if (flags & DBUS_WATCH_WRITABLE) { + mask |= POLLOUT; + } + + int fd = dbus_watch_get_unix_fd(watch); + struct pollfd pollfd = {fd, mask, 0}; + int index = watches->length++; + watches->fds[index] = pollfd; + watches->watches[index] = watch; + + sem_post(&watches->mutex); + return TRUE; +} + +static void remove_watch(DBusWatch *watch, void *data) { + struct subd_watches *watches = data; + sem_wait(&watches->mutex); + + int index = -1; + for (int i = 0; i < watches->length; ++i) { + if (watches->watches[i] == watch) { + index = i; + break; + } + } + + if (index != -1) { + --watches->length; + memmove(&watches->fds[index], &watches->fds[index + 1], + sizeof(struct pollfd) * watches->length - index); + memmove(&watches->watches[index], &watches->watches[index + 1], + sizeof(DBusWatch *) * watches->length - index); + } + + sem_post(&watches->mutex); +} + +static void toggle_watch(DBusWatch *watch, void *data) { + if (dbus_watch_get_enabled(watch)) { + add_watch(watch, data); + } else { + remove_watch(watch, data); + } +} + +struct subd_watches *subd_init_watches(struct DBusConnection *connection, + struct pollfd *fds, int size, DBusError *error) { + const char *error_code = NULL; + + // Initialize the watches structure. + struct subd_watches *watches = malloc(sizeof(struct subd_watches)); + if (watches == NULL) { + error_code = DBUS_ERROR_NO_MEMORY; + goto error; + } + + watches->capacity = 10 + size; + watches->length = size; + watches->fds = malloc(watches->capacity * sizeof(struct pollfd)); + watches->watches = malloc(watches->capacity * sizeof(DBusWatch*)); + if (watches->fds == NULL || watches->watches == NULL) { + error_code = DBUS_ERROR_NO_MEMORY; + goto error; + } + + // Initialize a semaphore for the watches. It is needed to prevent the add, + // remove and toggle functions accessing the watch storage while it is being + // processed by subd_process_watches. + if (sem_init(&watches->mutex, 0, 1) == -1) { + if (errno == EINVAL) { + error_code = DBUS_ERROR_INVALID_ARGS; + } else { + error_code = DBUS_ERROR_NO_MEMORY; + } + goto error; + } + + // Add any non-dbus file descriptors. + for (int i = 0; i < size; ++i) { + watches->fds[i] = (struct pollfd){ + .fd = fds->fd, + .events = fds->events + }; + watches->watches[i] = NULL; + ++fds; + } + + // Register the add, remove, and toggle functions. + // NOTE: Can't use the free_data_function argument to automatically free + // watches when connection finalizes, because sometimes it does not work. + if (!dbus_connection_set_watch_functions(connection, add_watch, + remove_watch, toggle_watch, watches, NULL)) { + error_code = DBUS_ERROR_NO_MEMORY; + goto error; + } + + return watches; + +error: + if (watches != NULL) { + if (watches->fds != NULL) { + free(watches->fds); + } + if (watches->watches != NULL) { + free(watches->watches); + } + } + dbus_set_error(error, error_code, NULL); + return NULL; +} + +void subd_process_watches(DBusConnection *conn, struct subd_watches *watches) { + sem_wait(&watches->mutex); + + for (int i = 0; i < watches->length; ++i) { + struct pollfd pollfd = watches->fds[i]; + DBusWatch *watch = watches->watches[i]; + + if (watch == NULL || !dbus_watch_get_enabled(watch)) { + continue; + } + + if (pollfd.revents & pollfd.events) { + unsigned int flags = 0; + if (pollfd.revents & POLLIN) { + flags |= DBUS_WATCH_READABLE; + } + if (pollfd.revents & POLLOUT) { + flags |= DBUS_WATCH_WRITABLE; + } + if (pollfd.revents & POLLHUP) { + flags |= DBUS_WATCH_HANGUP; + } + if (pollfd.revents & POLLERR) { + flags |= DBUS_WATCH_ERROR; + } + + //TODO: Error handling. Not sure, what is the right move here. Log + // and ignore? Make them fatal? + dbus_watch_handle(watch, flags); + while (dbus_connection_dispatch(conn) == + DBUS_DISPATCH_DATA_REMAINS); + } + } + + sem_post(&watches->mutex); +} diff --git a/include/subd-sdbus.h b/include/subd-sdbus.h new file mode 100644 index 0000000..8d77600 --- /dev/null +++ b/include/subd-sdbus.h @@ -0,0 +1,142 @@ +#ifndef _SUBD_SDBUS_H +#define _SUBD_SDBUS_H + +#include +#include + +#define SD_BUS_NAME_ALLOW_REPLACEMENT DBUS_NAME_FLAG_ALLOW_REPLACEMENT +#define SD_BUS_NAME_REPLACE_EXISTING DBUS_NAME_FLAG_REPLACE_EXISTING +#define SD_BUS_NAME_QUEUE ~DBUS_NAME_FLAG_DO_NOT_QUEUE + +/** + * These types are the rough equivalents of each other in libdbus and sd-bus. + * They are opaque structures in both library, so we can just typedef them to + * make our life easier. + */ +typedef struct DBusConnection sd_bus; +typedef struct DBusError sd_bus_error; +typedef struct subd_member sd_bus_vtable; + +/** + * his is not used, but typedef-d to preserve sd-bus function signatures. + */ +typedef int sd_bus_slot; + +/** + * In sd-bus, sd_bus_message knows what sd_bus it is associated with, and also + * keeps track of its current read/write position. This can be achieved with + * libdbus by encapsulating the DBusConnection (sd_bus), the DBusMessage, and a + * stack of iterators (implemented here as a wl_list of msg_iter structs) in one + * structure. The current DBusMessageIter (iter) is also stored to make + * accessing it easier (i.e. it's just syntactic sugar). + */ +struct msg_iter { + DBusMessageIter iter; + struct wl_list link; +}; + +typedef struct sd_bus_message { + sd_bus *bus; + DBusMessage *message; + DBusMessageIter *iter; + struct wl_list *iters; + int ref_count; +} sd_bus_message; + +/** + * The following two enums and one struct are pulled straight from subd, and are + * used by the handler dispatcher logic, and the automatic Introspectable + * intarface implementation. + * + * TODO: These could be changed to behave the same as sd-bus vtables, which + * would eliminate the need of #ifdef-d vtable definitions in dbus/xdg.c and + * dbus/mako.c. + */ + +/** + * Represents the three DBus member types. + */ +enum subd_member_type { + SUBD_METHOD, + SUBD_SIGNAL, + SUBD_PROPERTY, + SUBD_MEMBERS_END, +}; + +/** + * Represents the three possible access types for DBus properties. + */ +enum subd_property_access { + SUBD_PROPERTY_READ, + SUBD_PROPERTY_WRITE, + SUBD_PROPERTY_READWRITE, +}; + +/** + * This struct represents a DBus object member, and serves two purpose: + * + * - If the member is a method, it has a pointer to the method's handler + * function, so that the vtable dispatcher knows where to dispatch execution. + * - It has the metadata for the member (name, output and/or input signatures, + * access) that introspection data can be built from. + */ +struct subd_member { + enum subd_member_type type; + union { + struct { + const char *name; + int (*handler)(sd_bus_message *, void *, sd_bus_error *); + const char *input_signature; + const char *output_signature; + } m; + struct { + const char *name; + const char *signature; + } s; + struct { + const char *name; + const char *signature; + enum subd_property_access access; + } p; + int e; + }; +}; + +/** + * The following functions are wrappers around the libdbus library that mimic + * their sd-bus equivalents. These are not necessarily 1:1 matches of the + * originals, only the functionality needed for mako is implemented here. + * Most notable difference is that functions that read or write messages are + * only able to deal with basic types. + */ + +int sd_bus_open_user(sd_bus **bus); +int sd_bus_request_name(sd_bus *bus, const char *name, int flags); +void sd_bus_slot_unref(int *slot); +void sd_bus_flush_close_unref(sd_bus *bus); + +void sd_bus_error_set_const(sd_bus_error *err, const char *name, const char *msg); + +int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newmsg); +int sd_bus_message_open_container(sd_bus_message *msg, char type, + const char *signature); +int sd_bus_message_append(sd_bus_message *msg, const char *signature, ...); +int sd_bus_message_close_container(sd_bus_message *msg); +int sd_bus_message_peek_type(sd_bus_message *msg, void *unused, + const char **signature); +int sd_bus_message_read(sd_bus_message *msg, const char *signature, ...); +int sd_bus_message_enter_container(sd_bus_message *msg, char type, + const char *signature); +int sd_bus_message_exit_container(sd_bus_message *msg); +int sd_bus_message_skip(sd_bus_message *msg, const char *signature); +void sd_bus_message_unref(sd_bus_message *msg); + +int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, + const char *name, const char *signature, ...); +int sd_bus_reply_method_return(sd_bus_message *msg, const char *signature, ...); +int sd_bus_send(sd_bus *bus, sd_bus_message *msg, dbus_uint32_t *cookie); + +int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path_name, + const char *interface, const sd_bus_vtable *vtable, void *data); + +#endif diff --git a/include/subd.h b/include/subd.h new file mode 100644 index 0000000..33c997b --- /dev/null +++ b/include/subd.h @@ -0,0 +1,41 @@ +#ifndef _SUBD_H +#define _SUBD_H + +#include +#include "subd-sdbus.h" + +struct pollfd; + +/** + * This struct stores the DBus watches, and their corresponging file + * descriptors. This will be auto-updated whenever DBus needs additional file + * descriptors to watch. You can also store other, not DBus-related file + * descriptors here, so you can directly use fds as a parameter for poll(). + * If you add non-watch file descriptors, make sure to set their corresponding + * watch to NULL, so that #subd_process_watches knows to skip them. + */ +struct subd_watches { + struct pollfd *fds; + DBusWatch **watches; + int capacity; + int length; + sem_t mutex; +}; + +/** + * This function creates a subd_watches instance, and pre-populates it with + * the non-DBus file descriptors passed as fds. It also registers the add, + * remove and toggle functions that will handle automatic file descriptor + * additions/removals. + */ +struct subd_watches *subd_init_watches(DBusConnection *conn, struct pollfd *fds, + int size, DBusError *error); + +/** + * This function should be called from the event loop after a successful poll to + * handle the DBus watches that need to be handled (= the watches whose file + * descriptor returned an event). + */ +void subd_process_watches(DBusConnection *conn, struct subd_watches *watches); + +#endif From 5ed385e6ddf97378b9a61295fe77216a3525c394 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 26 Oct 2018 15:52:04 +0200 Subject: [PATCH 02/17] Add conditionally compiled code pieces for subd This commit adds necessary modifications to the original Mako code, and puts them behind preprocessor conditions defined by the build system. --- dbus/mako.c | 10 +++++++++ dbus/xdg.c | 12 +++++++++++ event-loop.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ include/dbus.h | 2 ++ include/event-loop.h | 9 ++++++++ include/mako.h | 2 ++ 6 files changed, 85 insertions(+) diff --git a/dbus/mako.c b/dbus/mako.c index 2b78ac6..c14f146 100644 --- a/dbus/mako.c +++ b/dbus/mako.c @@ -92,6 +92,7 @@ static int handle_reload(sd_bus_message *msg, void *data, return sd_bus_reply_method_return(msg, ""); } +#if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) static const sd_bus_vtable service_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD("DismissAllNotifications", "", "", handle_dismiss_all_notifications, SD_BUS_VTABLE_UNPRIVILEGED), @@ -100,6 +101,15 @@ static const sd_bus_vtable service_vtable[] = { SD_BUS_METHOD("Reload", "", "", handle_reload, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; +#else +static const struct subd_member service_vtable[] = { + {SUBD_METHOD, .m = {"DismissAllNotifications", handle_dismiss_all_notifications, "", ""}}, + {SUBD_METHOD, .m = {"DismissLastNotification", handle_dismiss_last_notification, "", ""}}, + {SUBD_METHOD, .m = {"InvokeAction", handle_invoke_action, "s", ""}}, + {SUBD_METHOD, .m = {"Reload", handle_reload, "", ""}}, + {SUBD_MEMBERS_END, .e=0}, +}; +#endif int init_dbus_mako(struct mako_state *state) { return sd_bus_add_object_vtable(state->bus, &state->mako_slot, service_path, diff --git a/dbus/xdg.c b/dbus/xdg.c index 66c2a9e..ab4abae 100644 --- a/dbus/xdg.c +++ b/dbus/xdg.c @@ -275,6 +275,7 @@ static int handle_get_server_information(sd_bus_message *msg, void *data, spec_version); } +#if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) static const sd_bus_vtable service_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD("GetCapabilities", "", "as", handle_get_capabilities, SD_BUS_VTABLE_UNPRIVILEGED), @@ -285,6 +286,17 @@ static const sd_bus_vtable service_vtable[] = { SD_BUS_SIGNAL("NotificationClosed", "uu", 0), SD_BUS_VTABLE_END }; +#else +static const struct subd_member service_vtable[] = { + {SUBD_METHOD, .m = {"GetCapabilities", handle_get_capabilities, "", "as"}}, + {SUBD_METHOD, .m = {"Notify", handle_notify, "susssasa{sv}i", "u"}}, + {SUBD_METHOD, .m = {"CloseNotification", handle_close_notification, "u", ""}}, + {SUBD_METHOD, .m = {"GetServerInformation", handle_get_server_information, "", "ssss"}}, + {SUBD_SIGNAL, .s = {"ActionInvoked", "us"}}, + {SUBD_SIGNAL, .s = {"NotificationClosed", "uu"}}, + {SUBD_MEMBERS_END, .e=0}, +}; +#endif int init_dbus_xdg(struct mako_state *state) { return sd_bus_add_object_vtable(state->bus, &state->xdg_slot, service_path, diff --git a/event-loop.c b/event-loop.c index aacfebf..4b6c02e 100644 --- a/event-loop.c +++ b/event-loop.c @@ -33,6 +33,7 @@ static int init_signalfd() { return sfd; } +#if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) bool init_event_loop(struct mako_event_loop *loop, sd_bus *bus, struct wl_display *display) { if ((loop->sfd = init_signalfd()) == -1) { @@ -65,6 +66,47 @@ bool init_event_loop(struct mako_event_loop *loop, sd_bus *bus, return true; } +#else +bool init_event_loop(struct mako_event_loop *loop, sd_bus *bus, + struct wl_display *display) { + if ((loop->sfd = init_signalfd()) == -1) { + return false; + } + + struct pollfd pollfds[MAKO_EVENT_COUNT]; + + pollfds[MAKO_EVENT_SIGNAL] = (struct pollfd){ + .fd = loop->sfd; + .events = POLLIN, + }; + + pollfds[MAKO_EVENT_WAYLAND] = (struct pollfd){ + .fd = wl_display_get_fd(display), + .events = POLLIN, + }; + + pollfds[MAKO_EVENT_TIMER] = (struct pollfd){ + .fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC), + .events = POLLIN, + }; + + DBusError error; + dbus_error_init(&error); + loop->watches = subd_init_watches(connection, pollfds, MAKO_EVENT_COUNT, + &error); + if (loop->watches == NULL) { + fprintf(stderr, "failed to initialize loop: %s\n", error.message); + return false; + } + + loop->fds = loop->watches->fds; + loop->bus = connection; + loop->display = display; + wl_list_init(&loop->timers); + + return true; +} +#endif void finish_event_loop(struct mako_event_loop *loop) { close(loop->fds[MAKO_EVENT_TIMER].fd); @@ -77,7 +119,11 @@ void finish_event_loop(struct mako_event_loop *loop) { } static int poll_event_loop(struct mako_event_loop *loop) { +#if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) return poll(loop->fds, MAKO_EVENT_COUNT, -1); +#else + return poll(loop->fds, loop->watches->length, -1); +#endif } static void timespec_add(struct timespec *t, int delta_ms) { @@ -215,6 +261,7 @@ int run_event_loop(struct mako_event_loop *loop) { wl_display_cancel_read(loop->display); } +#if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) if (loop->fds[MAKO_EVENT_DBUS].revents & POLLIN) { while (1) { ret = sd_bus_process(loop->bus, NULL); @@ -233,6 +280,9 @@ int run_event_loop(struct mako_event_loop *loop) { break; } } +#else + subd_process_watches(loop->bus, loop->watches); +#endif if (loop->fds[MAKO_EVENT_WAYLAND].revents & POLLIN) { ret = wl_display_read_events(loop->display); diff --git a/include/dbus.h b/include/dbus.h index 550151e..f50d79e 100644 --- a/include/dbus.h +++ b/include/dbus.h @@ -6,6 +6,8 @@ #include #elif HAVE_ELOGIND #include +#elif HAVE_SUBD +#include "subd.h" #endif struct mako_state; diff --git a/include/event-loop.h b/include/event-loop.h index 460c711..85d05ce 100644 --- a/include/event-loop.h +++ b/include/event-loop.h @@ -9,10 +9,14 @@ #include #elif HAVE_ELOGIND #include +#elif HAVE_SUBD +#include "subd.h" #endif enum mako_event { +#if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) MAKO_EVENT_DBUS, +#endif MAKO_EVENT_WAYLAND, MAKO_EVENT_TIMER, MAKO_EVENT_SIGNAL, @@ -20,7 +24,12 @@ enum mako_event { }; struct mako_event_loop { +#if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) struct pollfd fds[MAKO_EVENT_COUNT]; +#else + struct subd_watches *watches; + struct pollfd *fds; +#endif sd_bus *bus; struct wl_display *display; int sfd; diff --git a/include/mako.h b/include/mako.h index 9b81545..82eb0e5 100644 --- a/include/mako.h +++ b/include/mako.h @@ -7,6 +7,8 @@ #include #elif HAVE_ELOGIND #include +#elif HAVE_SUBD +#include "subd.h" #endif #include "config.h" From b9f3e39a7559934956cd43ba0c591d74047f3f3a Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 26 Oct 2018 17:07:20 +0200 Subject: [PATCH 03/17] Add dbus-1 as optional dependency This commit adds the dbus-1 library as a dependency if neither systemd, nor elogind is available. It also adds the sd-bus wrappers to the build system in such case. --- meson.build | 73 +++++++++++++++++++++++++++++------------------ meson_options.txt | 2 ++ 2 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 meson_options.txt diff --git a/meson.build b/meson.build index 1eecabc..a92dbc7 100644 --- a/meson.build +++ b/meson.build @@ -19,43 +19,62 @@ mako_inc = include_directories('include') cairo = dependency('cairo') pango = dependency('pango') pangocairo = dependency('pangocairo') +sdbus = dependency(get_option('sdbus-provider'), required: get_option('sdbus')) +dbus1 = dependency('dbus-1', required: not sdbus.found()) wayland_client = dependency('wayland-client') wayland_protos = dependency('wayland-protocols', version: '>=1.14') -logind = dependency('libsystemd', required: false) -if logind.found() - add_project_arguments('-DHAVE_SYSTEMD=1', language : 'c') +mako_deps = [] +mako_files = [] + +if sdbus.found() + mako_deps += sdbus + add_project_arguments('-DHAVE_' + get_option('logind-provider').to_upper(), language: 'c') else - logind = dependency('libelogind') - add_project_arguments('-DHAVE_ELOGIND=1', language : 'c') + mako_deps += dbus1 + mako_files += [ + 'dbus/subd-vtable.c', + 'dbus/subd-watch.c', + 'dbus/subd-sdbus.c', + ] + add_project_arguments('-DHAVE_SUBD', language: 'c') endif subdir('protocol') +mako_deps += [ + cairo, + client_protos, + sdbus, + pango, + pangocairo, + wayland_client, +] + +mako_files += [ + 'config.c', + 'dbus/dbus.c', + 'dbus/mako.c', + 'dbus/xdg.c', + 'event-loop.c', + 'main.c', + 'notification.c', + 'pool-buffer.c', + 'render.c', + 'wayland.c', + 'criteria.c', + 'types.c', +] + +# epoll is a separate library in FreeBSD +if host_machine.system() == 'freebsd' + mako_deps += dependency('epoll-shim') +endif + executable( 'mako', - files([ - 'config.c', - 'event-loop.c', - 'dbus/dbus.c', - 'dbus/mako.c', - 'dbus/xdg.c', - 'main.c', - 'notification.c', - 'pool-buffer.c', - 'render.c', - 'wayland.c', - 'criteria.c', - 'types.c', - ]), - dependencies: [ - cairo, - client_protos, - logind, - pango, - pangocairo, - wayland_client, - ], + files(mako_files), + dependencies: mako_deps, include_directories: [mako_inc], install: true, ) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..6a43a8c --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,2 @@ +option('sdbus', type: 'feature', value: 'auto', description: 'Use sd-bus library') +option('sdbus-provider', type: 'combo', choices: ['systemd', 'elogind'], value: 'systemd', description: 'Provider of sd-bus support library') From 937a6bcf027391e4230588cc15d7109d85c05b54 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 26 Oct 2018 17:18:01 +0200 Subject: [PATCH 04/17] Fix some minor compile errors in event-loop.c --- event-loop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/event-loop.c b/event-loop.c index 4b6c02e..f873ea1 100644 --- a/event-loop.c +++ b/event-loop.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,7 @@ bool init_event_loop(struct mako_event_loop *loop, sd_bus *bus, struct pollfd pollfds[MAKO_EVENT_COUNT]; pollfds[MAKO_EVENT_SIGNAL] = (struct pollfd){ - .fd = loop->sfd; + .fd = loop->sfd, .events = POLLIN, }; @@ -92,15 +93,14 @@ bool init_event_loop(struct mako_event_loop *loop, sd_bus *bus, DBusError error; dbus_error_init(&error); - loop->watches = subd_init_watches(connection, pollfds, MAKO_EVENT_COUNT, - &error); + loop->watches = subd_init_watches(bus, pollfds, MAKO_EVENT_COUNT, &error); if (loop->watches == NULL) { fprintf(stderr, "failed to initialize loop: %s\n", error.message); return false; } loop->fds = loop->watches->fds; - loop->bus = connection; + loop->bus = bus; loop->display = display; wl_list_init(&loop->timers); From 70375e0d9103a3d6636b92ebfcb8c8d85a1473e1 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 26 Oct 2018 18:26:50 +0200 Subject: [PATCH 05/17] Fix POSIX feature test macros This commit applies fixes for the following: pool-buffer.c: mkstemp is in POSIX.1-2008, thus requires _POSIX_C_SOURCE to be 200809L or higher, and _X_OPEN_SOURCE 500 does not provide that. event-loop.c: TFD_CLOEXEC is defined as O_CLOEXEC, which also appeared in POSIX.1-2008, thus _POSIX_C_SOURCE 199309L is not enough. --- event-loop.c | 2 +- pool-buffer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/event-loop.c b/event-loop.c index f873ea1..11f49b7 100644 --- a/event-loop.c +++ b/event-loop.c @@ -1,4 +1,4 @@ -#define _POSIX_C_SOURCE 199309L +#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/pool-buffer.c b/pool-buffer.c index d405900..45a8282 100644 --- a/pool-buffer.c +++ b/pool-buffer.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _POSIX_C_SOURCE 200809L #include #include #include From dcef8379be34a2cf3ab7770f58ce6444be1a2489 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 26 Oct 2018 18:31:26 +0200 Subject: [PATCH 06/17] Add a makoctl version that uses dbus-send busctl is part of sd-bus, so we don't have it when we are using subd. --- makoctl.libdbus | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ meson.build | 7 +++++-- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100755 makoctl.libdbus diff --git a/makoctl.libdbus b/makoctl.libdbus new file mode 100755 index 0000000..3d92d24 --- /dev/null +++ b/makoctl.libdbus @@ -0,0 +1,52 @@ +#!/bin/sh + +set -e + +usage() { + echo "Usage: makoctl [options...]" + echo "" + echo "Commands:" + echo " dismiss [-a|--all] Dismiss the last or all notifications" + echo " invoke [action] Invoke an action on the last notification" + echo " reload Reload the configuration file" + echo " help Show this help" +} + +call() { + dbus-send --session --dest=org.freedesktop.Notifications /fr/emersion/Mako \ + fr.emersion.Mako."$@" +} + +case "$1" in +"dismiss") + case "$2" in + "-a"|"--all") + call DismissAllNotifications + ;; + "") + call DismissLastNotification + ;; + *) + echo "makoctl: unrecognized option '$2'" + exit 1 + ;; + esac + ;; +"invoke") + action="$2" + if [ -z "$action" ] ; then + action="default" + fi + call InvokeAction "s" "$action" + ;; +"reload") + call Reload + ;; +"help"|"--help"|"-h") + usage + ;; +*) + echo "makoctl: unrecognized command '$1'" + exit 1 + ;; +esac diff --git a/meson.build b/meson.build index a92dbc7..5ccbea2 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ project( 'c', version: '1.1.0', license: 'MIT', - meson_version: '>=0.43.0', + meson_version: '>=0.47.0', default_options: [ 'c_std=c11', 'warning_level=2', @@ -79,8 +79,11 @@ executable( install: true, ) +makoctl = sdbus.found() ? 'makoctl' : 'makoctl.libdbus' + install_data( - 'makoctl', + makoctl, + rename: 'makoctl', install_dir: get_option('bindir'), install_mode: 'rwxr-xr-x', ) From 8435daa0f9f9e7d12103a6710d76bb5b9ddc7dbf Mon Sep 17 00:00:00 2001 From: sghctoma Date: Sun, 28 Oct 2018 19:46:43 +0100 Subject: [PATCH 07/17] Avoid sending a non-set DBusError as reply --- dbus/subd-vtable.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dbus/subd-vtable.c b/dbus/subd-vtable.c index 91b7c0e..6eab057 100644 --- a/dbus/subd-vtable.c +++ b/dbus/subd-vtable.c @@ -189,11 +189,12 @@ static bool call_method(const struct subd_member *member, DBusConnection *conn, DBusError error; dbus_error_init(&error); if (member->m.handler(message, userdata, &error) < 0) { - //XXX: handle error == NULL - DBusMessage *error_message = - dbus_message_new_error(msg, error.name, error.message); - dbus_connection_send(conn, error_message, 0); - dbus_error_free(&error); + if (dbus_error_is_set(&error)) { + DBusMessage *error_message = + dbus_message_new_error(msg, error.name, error.message); + dbus_connection_send(conn, error_message, 0); + dbus_error_free(&error); + } return false; } return true; From c186e763881d7a8fffba3f1b5abdef89b8f4986e Mon Sep 17 00:00:00 2001 From: sghctoma Date: Mon, 29 Oct 2018 09:52:58 +0100 Subject: [PATCH 08/17] Free wl_list on error and message unref --- dbus/subd-sdbus.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/dbus/subd-sdbus.c b/dbus/subd-sdbus.c index eccdc14..3df1a0f 100644 --- a/dbus/subd-sdbus.c +++ b/dbus/subd-sdbus.c @@ -41,6 +41,8 @@ void sd_bus_error_set_const(sd_bus_error *err, const char *name, const char *msg int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newmsg) { sd_bus_message *new_message = NULL; DBusMessage *message = NULL; + struct msg_iter *iter = NULL; + // Allocate a new "method return" DBusMessage if ((message = dbus_message_new_method_return(msg->message)) == NULL) { goto error; @@ -58,7 +60,7 @@ int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newms new_message->message = message; new_message->ref_count = 1; - //Also initialize the sd_bus_message's iterator stack, ... + //Also initialize the sd_bus_message's iterator stack, ... new_message->iters = malloc(sizeof(struct wl_list)); if (new_message->iters == NULL) { goto error; @@ -66,7 +68,7 @@ int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newms wl_list_init(new_message->iters); // ... and push a new append iterator to it. - struct msg_iter *iter = malloc(sizeof(struct msg_iter)); + iter = malloc(sizeof(struct msg_iter)); if (iter == NULL) { goto error; } @@ -78,7 +80,8 @@ int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newms return 0; error: - //XXX list_destroy(new_message->iters); + free(iter); + free(new_message->iters); free(new_message); dbus_message_unref(message); *newmsg = NULL; @@ -152,8 +155,12 @@ int sd_bus_message_append(sd_bus_message *msg, const char *signature, ...) { void sd_bus_message_unref(sd_bus_message *msg) { dbus_message_unref(msg->message); - if (msg->ref_count == 0) { - //XXX list_destroy(msg->iters); + if (--msg->ref_count == 0) { + struct msg_iter *iter, *itertmp = NULL; + wl_list_for_each_safe(iter, itertmp, msg->iters, link) { + free(iter); + } + free(msg->iters); free(msg); } } From 485e668644033a0d2d5c56d0ddeeb3da46c197a4 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Mon, 29 Oct 2018 09:59:47 +0100 Subject: [PATCH 09/17] Remove TODO about dbus_watch_handle error handling A call to dbus_watch_handle fails if there is not enough memory. In such case, the "application may spin in a busy loop on the file descriptor until memory becomes available, but nothing more catastrophic should happen" (from libdbus documentation). I think this is better, than potentially blocking the handling of other fd events with a sleep. --- dbus/subd-watch.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dbus/subd-watch.c b/dbus/subd-watch.c index ef913e7..5b6190e 100644 --- a/dbus/subd-watch.c +++ b/dbus/subd-watch.c @@ -168,9 +168,8 @@ void subd_process_watches(DBusConnection *conn, struct subd_watches *watches) { flags |= DBUS_WATCH_ERROR; } - //TODO: Error handling. Not sure, what is the right move here. Log - // and ignore? Make them fatal? dbus_watch_handle(watch, flags); + while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS); } From 0a371a4802615d718edef2af540d636925da9604 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Mon, 29 Oct 2018 14:01:48 +0100 Subject: [PATCH 10/17] Refactor sd_bus_add_object_vtable This function was too big, and also didn't do all the necessary resource deallocation in case of errors. --- dbus/subd-vtable.c | 165 ++++++++++++++++++++++++------------------- include/subd-sdbus.h | 2 +- 2 files changed, 94 insertions(+), 73 deletions(-) diff --git a/dbus/subd-vtable.c b/dbus/subd-vtable.c index 6eab057..c2aed8d 100644 --- a/dbus/subd-vtable.c +++ b/dbus/subd-vtable.c @@ -236,10 +236,75 @@ static const DBusObjectPathVTable dbus_vtable = { .unregister_function = NULL, }; +static struct path *register_new_path(sd_bus * bus, const char *path_name, + void *userdata) { + struct wl_list *interfaces = NULL; + struct interface *new_interface = NULL; + struct path *new_path = NULL; + struct vtable_userdata *data = NULL; + + // We create an interface list, and append Introspectable to it. We want + // every path to implement org.freedesktop.DBus.Introspectable. + // TODO: Also implement the following: + // - org.freedesktop.DBus.Peer + // - org.freedesktop.DBus.Properties + interfaces = malloc(sizeof(struct wl_list)); + if (interfaces == NULL) { + goto error; + } + wl_list_init(interfaces); + + new_interface = malloc(sizeof(struct interface)); + if (new_interface == NULL) { + goto error; + } + + new_interface->name = strdup("org.freedesktop.DBus.Introspectable"); + new_interface->members = introspectable_members; + wl_list_insert(interfaces, &new_interface->link); + + // Create the path with the new interface list + new_path = malloc(sizeof(struct path)); + if (new_path == NULL) { + goto error; + } + + new_path->path = strdup(path_name); + new_path->interfaces = interfaces; + new_path->introspection_data = NULL; // This will be set later. + + // ... and also register it. + // We merge userdata with the interfaces list in a struct + // vtable_userdata here, so vtable_dispatch will know what interfaces to + // traverse, and what userdata to pass to the actual handler functions. + data = malloc(sizeof(struct vtable_userdata)); + if (data == NULL) { + goto error; + } + + data->interfaces = interfaces; + data->userdata = userdata; + if (!dbus_connection_try_register_object_path(bus, path_name, &dbus_vtable, + data, NULL)) { + goto error; + } + + // Append the path to the list of paths + wl_list_insert(paths, &new_path->link); + + return new_path; + +error: + free(data); + free(new_path); + free(new_interface); + free(interfaces); + return NULL; +} + int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, - const char *path_name, const char *interface, + const char *path_name, const char *interface_name, const sd_bus_vtable *vtable, void *userdata) { - int ret = 0; if (paths == NULL) { paths = malloc(sizeof(struct path)); @@ -249,90 +314,46 @@ int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, wl_list_init(paths); } - // See if this path is already registered. If it is, it must have at least - // one interface, so get a pointer to the interfaces list. - struct wl_list *interfaces = NULL; + // See if this path is already registered, ... struct path *p, *path = NULL; wl_list_for_each(p, paths, link) { if (strcmp(path_name, p->path) == 0) { path = p; - interfaces = p->interfaces; break; } } - if (interfaces == NULL) { - // Path was not registered before, so first we create an interface list, - // and append Introspectable to it. We want every path to implement - // org.freedesktop.DBus.Introspectable. - // TODO: Also implement the following: - // - org.freedesktop.DBus.Peer - // - org.freedesktop.DBus.Properties - interfaces = malloc(sizeof(struct wl_list)); - if (interfaces == NULL) { - ret = -ENOMEM; - goto finish; - } - wl_list_init(interfaces); - struct interface *new_interface = malloc(sizeof(struct interface)); - if (interface == NULL) { - ret = -ENOMEM; - goto finish; - } - new_interface->name = strdup("org.freedesktop.DBus.Introspectable"); - new_interface->members = introspectable_members; - wl_list_insert(interfaces, &new_interface->link); - - // Create the path with the new interface list - struct path *new_path = malloc(sizeof(struct path)); - if (new_path == NULL) { - ret = -ENOMEM; - goto finish; - } - new_path->path = strdup(path_name); - new_path->interfaces = interfaces; - new_path->introspection_data = NULL; // This will be set later. - path = new_path; - - // ... and also register it. - // We merge userdata with the interfaces list in a struct - // vtable_userdata here, so vtable_dispatch will know what interfaces to - // traverse, and what userdata to pass to the actual handler functions. - struct vtable_userdata *data = malloc(sizeof(struct vtable_userdata)); - if (data == NULL) { - free(new_path); - ret = -ENOMEM; - goto finish; - } - data->interfaces = interfaces; - data->userdata = userdata; - if (!dbus_connection_try_register_object_path(bus, path_name, - &dbus_vtable, data, NULL)) { - free(new_path); - ret = -ENXIO; - goto finish; - } + // ... and if it is not, register it. + if (path == NULL && + (path = register_new_path(bus, path_name, userdata)) == NULL) { + return -ENOMEM; } - //TODO: Decide what to do if interface is already registered. Replace - //memeber list? Append new members to list? Throw an error? - - // Append the new interface to the path's interface list. - struct interface *new_interface = malloc(sizeof(struct interface)); - if (new_interface == NULL) { - ret = -ENOMEM; - goto finish; + // See if the path already has an interface with this name, ... + struct interface *i, *interface = NULL; + wl_list_for_each(i, path->interfaces, link) { + if (strcmp(i->name, interface_name) == 0) { + interface = i; + break; + } } - new_interface->name = strdup(interface); - new_interface->members = vtable; - wl_list_insert(interfaces, &new_interface->link); - // Append the path to the list of paths - wl_list_insert(paths, &path->link); + // ... and create it, if not. If it does, replace the vtable for the + // existing interface. + if (interface == NULL) { + interface = malloc(sizeof(struct interface)); + if (interface == NULL) { + return -ENOMEM; + } + interface->name = strdup(interface_name); + interface->members = vtable; + wl_list_insert(path->interfaces, &interface->link); + } else { + interface->members = vtable; + } // (Re)generate introspection XML for this path. generate_introspection_data(path); -finish: - return ret; + return 0; } diff --git a/include/subd-sdbus.h b/include/subd-sdbus.h index 8d77600..e7d177d 100644 --- a/include/subd-sdbus.h +++ b/include/subd-sdbus.h @@ -137,6 +137,6 @@ int sd_bus_reply_method_return(sd_bus_message *msg, const char *signature, ...); int sd_bus_send(sd_bus *bus, sd_bus_message *msg, dbus_uint32_t *cookie); int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path_name, - const char *interface, const sd_bus_vtable *vtable, void *data); + const char *interface_name, const sd_bus_vtable *vtable, void *userdata); #endif From 6e7c60cbbad548a21bec96b3b34d6c074ab42a3c Mon Sep 17 00:00:00 2001 From: sghctoma Date: Mon, 29 Oct 2018 14:40:36 +0100 Subject: [PATCH 11/17] Add feature test macros where necessary strdup requires _POSIX_C_SOURCE 200112L open_memstream requires _POSIX_C_SOURCE 200809L --- dbus/subd-sdbus.c | 1 + dbus/subd-vtable.c | 1 + 2 files changed, 2 insertions(+) diff --git a/dbus/subd-sdbus.c b/dbus/subd-sdbus.c index 3df1a0f..a6cb0a5 100644 --- a/dbus/subd-sdbus.c +++ b/dbus/subd-sdbus.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200112L #include #include #include diff --git a/dbus/subd-vtable.c b/dbus/subd-vtable.c index c2aed8d..684f26d 100644 --- a/dbus/subd-vtable.c +++ b/dbus/subd-vtable.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include From fa8ba987e3fcb8c584f3342ebd0ae5b3b5c61998 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Mon, 29 Oct 2018 17:34:30 +0100 Subject: [PATCH 12/17] Fix actions Actions were not working, because I misunderstood the return values of the sd_bus_message_read function. The makoctl.libdbus script also needed a fix because dbus-send's syntax for message arguments differs from busctl's. --- dbus/subd-sdbus.c | 2 +- makoctl.libdbus | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dbus/subd-sdbus.c b/dbus/subd-sdbus.c index a6cb0a5..7282a69 100644 --- a/dbus/subd-sdbus.c +++ b/dbus/subd-sdbus.c @@ -213,7 +213,7 @@ int sd_bus_message_readv(sd_bus_message *msg, const char *signature, va_list ap) dbus_message_iter_next(msg->iter); } while (dbus_signature_iter_next(&iter)); - return dbus_message_iter_has_next(msg->iter); + return 1; } int sd_bus_message_read(sd_bus_message *msg, const char *signature, ...) { diff --git a/makoctl.libdbus b/makoctl.libdbus index 3d92d24..6c287d0 100755 --- a/makoctl.libdbus +++ b/makoctl.libdbus @@ -37,7 +37,7 @@ case "$1" in if [ -z "$action" ] ; then action="default" fi - call InvokeAction "s" "$action" + call InvokeAction "string:$action" ;; "reload") call Reload From 6317f150471fcf9dfe77ff93e4683246d37ee0c3 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Tue, 30 Oct 2018 17:49:47 +0100 Subject: [PATCH 13/17] Change iters field from wl_list* to wl_list There's no need to dynamically allocate the list that represents the message's iterator stack in sd_bus_message. +bonus: Fixed two typos. --- dbus/subd-sdbus.c | 24 +++++++++--------------- dbus/subd-vtable.c | 10 +++------- include/subd-sdbus.h | 4 ++-- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/dbus/subd-sdbus.c b/dbus/subd-sdbus.c index 7282a69..012f3e3 100644 --- a/dbus/subd-sdbus.c +++ b/dbus/subd-sdbus.c @@ -62,11 +62,7 @@ int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newms new_message->ref_count = 1; //Also initialize the sd_bus_message's iterator stack, ... - new_message->iters = malloc(sizeof(struct wl_list)); - if (new_message->iters == NULL) { - goto error; - } - wl_list_init(new_message->iters); + wl_list_init(&new_message->iters); // ... and push a new append iterator to it. iter = malloc(sizeof(struct msg_iter)); @@ -74,7 +70,7 @@ int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newms goto error; } dbus_message_iter_init_append(new_message->message, &iter->iter); - wl_list_insert(new_message->iters, &iter->link); + wl_list_insert(&new_message->iters, &iter->link); new_message->iter = &iter->iter; *newmsg = new_message; @@ -82,7 +78,6 @@ int sd_bus_message_new_method_return(sd_bus_message *msg, sd_bus_message **newms error: free(iter); - free(new_message->iters); free(new_message); dbus_message_unref(message); *newmsg = NULL; @@ -99,7 +94,7 @@ int sd_bus_message_open_container(sd_bus_message *msg, char type, if (!dbus_message_iter_open_container(msg->iter, type, signature, &sub->iter)) { goto error; } - wl_list_insert(msg->iters, &sub->link); + wl_list_insert(&msg->iters, &sub->link); msg->iter = &sub->iter; return 0; @@ -111,11 +106,11 @@ int sd_bus_message_open_container(sd_bus_message *msg, char type, int sd_bus_message_close_container(sd_bus_message *msg) { // Remove the iterator we want to close from from the iterator stack - struct msg_iter *iter = wl_container_of(msg->iters->next, iter, link); + struct msg_iter *iter = wl_container_of(msg->iters.next, iter, link); wl_list_remove(&iter->link); // Set msg's current iterator to the top of the iterator stack. - struct msg_iter *new_head = wl_container_of(msg->iters->next, iter, link); + struct msg_iter *new_head = wl_container_of(msg->iters.next, iter, link); msg->iter = &new_head->iter; // Close the container. @@ -158,10 +153,9 @@ void sd_bus_message_unref(sd_bus_message *msg) { dbus_message_unref(msg->message); if (--msg->ref_count == 0) { struct msg_iter *iter, *itertmp = NULL; - wl_list_for_each_safe(iter, itertmp, msg->iters, link) { + wl_list_for_each_safe(iter, itertmp, &msg->iters, link) { free(iter); } - free(msg->iters); free(msg); } } @@ -292,7 +286,7 @@ int sd_bus_message_enter_container(sd_bus_message *msg, char type, } dbus_message_iter_recurse(msg->iter, &sub_iter->iter); - wl_list_insert(msg->iters, &sub_iter->link); + wl_list_insert(&msg->iters, &sub_iter->link); msg->iter = &sub_iter->iter; return dbus_message_iter_has_next(&sub_iter->iter); @@ -300,12 +294,12 @@ int sd_bus_message_enter_container(sd_bus_message *msg, char type, int sd_bus_message_exit_container(sd_bus_message *msg) { // Remove the iterator we want to exit from from the iterator stack - struct msg_iter *iter = wl_container_of(msg->iters->next, iter, link); + struct msg_iter *iter = wl_container_of(msg->iters.next, iter, link); wl_list_remove(&iter->link); // Set msg's current iterator to the top of the iterator stack, and use // dbus_message_iter_next to "step over" the iterator we are exiting. - iter = wl_container_of(msg->iters->next, iter, link); + iter = wl_container_of(msg->iters.next, iter, link); msg->iter = &iter->iter; dbus_message_iter_next(msg->iter); diff --git a/dbus/subd-vtable.c b/dbus/subd-vtable.c index 684f26d..f2b6d5a 100644 --- a/dbus/subd-vtable.c +++ b/dbus/subd-vtable.c @@ -173,18 +173,14 @@ static bool call_method(const struct subd_member *member, DBusConnection *conn, message->bus = conn; message->message = msg; message->ref_count = 1; - message->iters = malloc(sizeof(struct wl_list)); - if (message->iters == NULL) { - return false; - } - wl_list_init(message->iters); + wl_list_init(&message->iters); struct msg_iter *iter = malloc(sizeof(struct msg_iter)); if (iter == NULL) { return false; } dbus_message_iter_init(message->message, &iter->iter); - wl_list_insert(message->iters, &iter->link); + wl_list_insert(&message->iters, &iter->link); message->iter = &iter->iter; DBusError error; @@ -325,7 +321,7 @@ int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, } // ... and if it is not, register it. - if (path == NULL && + if (path == NULL && (path = register_new_path(bus, path_name, userdata)) == NULL) { return -ENOMEM; } diff --git a/include/subd-sdbus.h b/include/subd-sdbus.h index e7d177d..b676d61 100644 --- a/include/subd-sdbus.h +++ b/include/subd-sdbus.h @@ -18,7 +18,7 @@ typedef struct DBusError sd_bus_error; typedef struct subd_member sd_bus_vtable; /** - * his is not used, but typedef-d to preserve sd-bus function signatures. + * This is not used, but typedef-d to preserve sd-bus function signatures. */ typedef int sd_bus_slot; @@ -39,7 +39,7 @@ typedef struct sd_bus_message { sd_bus *bus; DBusMessage *message; DBusMessageIter *iter; - struct wl_list *iters; + struct wl_list iters; int ref_count; } sd_bus_message; From 38f4b397865d76a696126f09a5d992db96e7acf5 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Wed, 31 Oct 2018 06:14:16 +0100 Subject: [PATCH 14/17] Fix watch storage capacity increment The return value of successful realloc calls was ignored. This commit also replaces malloc with calloc for the DBusWatch * and pollfd arrays, and improves deallocation in case of errors. --- dbus/subd-watch.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/dbus/subd-watch.c b/dbus/subd-watch.c index 5b6190e..aa85c81 100644 --- a/dbus/subd-watch.c +++ b/dbus/subd-watch.c @@ -16,14 +16,24 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) { if (watches->length == watches->capacity) { int c = watches->capacity + 10; - void *t1 = realloc(watches->fds, sizeof(struct pollfd) * c); - void *t2 = realloc(watches->watches, sizeof(DBusWatch *) * c); - if (t1 == NULL || t2 == NULL) { + + void *new_fds = realloc(watches->fds, sizeof(struct pollfd) * c); + if (new_fds != NULL) { + watches->fds = new_fds; + } else { sem_post(&watches->mutex); return FALSE; + } + + void *new_watches = realloc(watches->watches, sizeof(DBusWatch *) * c); + if (new_watches != NULL) { + watches->watches = new_watches; } else { - watches->capacity = c; + sem_post(&watches->mutex); + return FALSE; } + + watches->capacity = c; } short mask = 0; @@ -89,8 +99,8 @@ struct subd_watches *subd_init_watches(struct DBusConnection *connection, watches->capacity = 10 + size; watches->length = size; - watches->fds = malloc(watches->capacity * sizeof(struct pollfd)); - watches->watches = malloc(watches->capacity * sizeof(DBusWatch*)); + watches->fds = calloc(watches->capacity, sizeof(struct pollfd)); + watches->watches = calloc(watches->capacity, sizeof(DBusWatch*)); if (watches->fds == NULL || watches->watches == NULL) { error_code = DBUS_ERROR_NO_MEMORY; goto error; @@ -131,12 +141,9 @@ struct subd_watches *subd_init_watches(struct DBusConnection *connection, error: if (watches != NULL) { - if (watches->fds != NULL) { - free(watches->fds); - } - if (watches->watches != NULL) { - free(watches->watches); - } + free(watches->fds); + free(watches->watches); + free(watches); } dbus_set_error(error, error_code, NULL); return NULL; From 0c5c364ca98854a4b30b83acade1be6a90adafcc Mon Sep 17 00:00:00 2001 From: sghctoma Date: Wed, 31 Oct 2018 16:02:40 +0100 Subject: [PATCH 15/17] Change subd_watches to subd_watch_store I use plurals in variable names to imply an array, or some kind of list, and this is a struct (that contains two arrays, but still). Also things like watches->watches could be confusing. --- dbus/subd-watch.c | 104 +++++++++++++++++++++---------------------- event-loop.c | 10 ++--- include/event-loop.h | 2 +- include/subd.h | 8 ++-- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/dbus/subd-watch.c b/dbus/subd-watch.c index aa85c81..718ea78 100644 --- a/dbus/subd-watch.c +++ b/dbus/subd-watch.c @@ -6,34 +6,34 @@ #include "subd.h" static dbus_bool_t add_watch(DBusWatch *watch, void *data) { - struct subd_watches *watches = data; - sem_wait(&watches->mutex); + struct subd_watch_store *watch_store = data; + sem_wait(&watch_store->mutex); if (!dbus_watch_get_enabled(watch)) { - sem_post(&watches->mutex); + sem_post(&watch_store->mutex); return TRUE; } - if (watches->length == watches->capacity) { - int c = watches->capacity + 10; + if (watch_store->length == watch_store->capacity) { + int c = watch_store->capacity + 10; - void *new_fds = realloc(watches->fds, sizeof(struct pollfd) * c); + void *new_fds = realloc(watch_store->fds, sizeof(struct pollfd) * c); if (new_fds != NULL) { - watches->fds = new_fds; + watch_store->fds = new_fds; } else { - sem_post(&watches->mutex); + sem_post(&watch_store->mutex); return FALSE; } - void *new_watches = realloc(watches->watches, sizeof(DBusWatch *) * c); + void *new_watches = realloc(watch_store->watches, sizeof(DBusWatch *) * c); if (new_watches != NULL) { - watches->watches = new_watches; + watch_store->watches = new_watches; } else { - sem_post(&watches->mutex); + sem_post(&watch_store->mutex); return FALSE; } - watches->capacity = c; + watch_store->capacity = c; } short mask = 0; @@ -47,35 +47,35 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) { int fd = dbus_watch_get_unix_fd(watch); struct pollfd pollfd = {fd, mask, 0}; - int index = watches->length++; - watches->fds[index] = pollfd; - watches->watches[index] = watch; + int index = watch_store->length++; + watch_store->fds[index] = pollfd; + watch_store->watches[index] = watch; - sem_post(&watches->mutex); + sem_post(&watch_store->mutex); return TRUE; } static void remove_watch(DBusWatch *watch, void *data) { - struct subd_watches *watches = data; - sem_wait(&watches->mutex); + struct subd_watch_store *watch_store = data; + sem_wait(&watch_store->mutex); int index = -1; - for (int i = 0; i < watches->length; ++i) { - if (watches->watches[i] == watch) { + for (int i = 0; i < watch_store->length; ++i) { + if (watch_store->watches[i] == watch) { index = i; break; } } if (index != -1) { - --watches->length; - memmove(&watches->fds[index], &watches->fds[index + 1], - sizeof(struct pollfd) * watches->length - index); - memmove(&watches->watches[index], &watches->watches[index + 1], - sizeof(DBusWatch *) * watches->length - index); + --watch_store->length; + memmove(&watch_store->fds[index], &watch_store->fds[index + 1], + sizeof(struct pollfd) * watch_store->length - index); + memmove(&watch_store->watches[index], &watch_store->watches[index + 1], + sizeof(DBusWatch *) * watch_store->length - index); } - sem_post(&watches->mutex); + sem_post(&watch_store->mutex); } static void toggle_watch(DBusWatch *watch, void *data) { @@ -86,22 +86,22 @@ static void toggle_watch(DBusWatch *watch, void *data) { } } -struct subd_watches *subd_init_watches(struct DBusConnection *connection, - struct pollfd *fds, int size, DBusError *error) { +struct subd_watch_store *subd_init_watches(DBusConnection *conn, + struct pollfd *fds, int size, DBusError *err) { const char *error_code = NULL; // Initialize the watches structure. - struct subd_watches *watches = malloc(sizeof(struct subd_watches)); - if (watches == NULL) { + struct subd_watch_store *watch_store = malloc(sizeof(struct subd_watch_store)); + if (watch_store == NULL) { error_code = DBUS_ERROR_NO_MEMORY; goto error; } - watches->capacity = 10 + size; - watches->length = size; - watches->fds = calloc(watches->capacity, sizeof(struct pollfd)); - watches->watches = calloc(watches->capacity, sizeof(DBusWatch*)); - if (watches->fds == NULL || watches->watches == NULL) { + watch_store->capacity = 10 + size; + watch_store->length = size; + watch_store->fds = calloc(watch_store->capacity, sizeof(struct pollfd)); + watch_store->watches = calloc(watch_store->capacity, sizeof(DBusWatch*)); + if (watch_store->fds == NULL || watch_store->watches == NULL) { error_code = DBUS_ERROR_NO_MEMORY; goto error; } @@ -109,7 +109,7 @@ struct subd_watches *subd_init_watches(struct DBusConnection *connection, // Initialize a semaphore for the watches. It is needed to prevent the add, // remove and toggle functions accessing the watch storage while it is being // processed by subd_process_watches. - if (sem_init(&watches->mutex, 0, 1) == -1) { + if (sem_init(&watch_store->mutex, 0, 1) == -1) { if (errno == EINVAL) { error_code = DBUS_ERROR_INVALID_ARGS; } else { @@ -120,41 +120,41 @@ struct subd_watches *subd_init_watches(struct DBusConnection *connection, // Add any non-dbus file descriptors. for (int i = 0; i < size; ++i) { - watches->fds[i] = (struct pollfd){ + watch_store->fds[i] = (struct pollfd){ .fd = fds->fd, .events = fds->events }; - watches->watches[i] = NULL; + watch_store->watches[i] = NULL; ++fds; } // Register the add, remove, and toggle functions. // NOTE: Can't use the free_data_function argument to automatically free // watches when connection finalizes, because sometimes it does not work. - if (!dbus_connection_set_watch_functions(connection, add_watch, - remove_watch, toggle_watch, watches, NULL)) { + if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch, + toggle_watch, watch_store, NULL)) { error_code = DBUS_ERROR_NO_MEMORY; goto error; } - return watches; + return watch_store; error: - if (watches != NULL) { - free(watches->fds); - free(watches->watches); - free(watches); + if (watch_store != NULL) { + free(watch_store->fds); + free(watch_store->watches); + free(watch_store); } - dbus_set_error(error, error_code, NULL); + dbus_set_error(err, error_code, NULL); return NULL; } -void subd_process_watches(DBusConnection *conn, struct subd_watches *watches) { - sem_wait(&watches->mutex); +void subd_process_watches(DBusConnection *conn, struct subd_watch_store *watch_store) { + sem_wait(&watch_store->mutex); - for (int i = 0; i < watches->length; ++i) { - struct pollfd pollfd = watches->fds[i]; - DBusWatch *watch = watches->watches[i]; + for (int i = 0; i < watch_store->length; ++i) { + struct pollfd pollfd = watch_store->fds[i]; + DBusWatch *watch = watch_store->watches[i]; if (watch == NULL || !dbus_watch_get_enabled(watch)) { continue; @@ -182,5 +182,5 @@ void subd_process_watches(DBusConnection *conn, struct subd_watches *watches) { } } - sem_post(&watches->mutex); + sem_post(&watch_store->mutex); } diff --git a/event-loop.c b/event-loop.c index 11f49b7..a9e2da7 100644 --- a/event-loop.c +++ b/event-loop.c @@ -93,13 +93,13 @@ bool init_event_loop(struct mako_event_loop *loop, sd_bus *bus, DBusError error; dbus_error_init(&error); - loop->watches = subd_init_watches(bus, pollfds, MAKO_EVENT_COUNT, &error); - if (loop->watches == NULL) { + loop->watch_store = subd_init_watches(bus, pollfds, MAKO_EVENT_COUNT, &error); + if (loop->watch_store == NULL) { fprintf(stderr, "failed to initialize loop: %s\n", error.message); return false; } - loop->fds = loop->watches->fds; + loop->fds = loop->watch_store->fds; loop->bus = bus; loop->display = display; wl_list_init(&loop->timers); @@ -122,7 +122,7 @@ static int poll_event_loop(struct mako_event_loop *loop) { #if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) return poll(loop->fds, MAKO_EVENT_COUNT, -1); #else - return poll(loop->fds, loop->watches->length, -1); + return poll(loop->fds, loop->watch_store->length, -1); #endif } @@ -281,7 +281,7 @@ int run_event_loop(struct mako_event_loop *loop) { } } #else - subd_process_watches(loop->bus, loop->watches); + subd_process_watches(loop->bus, loop->watch_store); #endif if (loop->fds[MAKO_EVENT_WAYLAND].revents & POLLIN) { diff --git a/include/event-loop.h b/include/event-loop.h index 85d05ce..1de870b 100644 --- a/include/event-loop.h +++ b/include/event-loop.h @@ -27,7 +27,7 @@ struct mako_event_loop { #if defined(HAVE_SYSTEMD) || defined(HAVE_ELOGIND) struct pollfd fds[MAKO_EVENT_COUNT]; #else - struct subd_watches *watches; + struct subd_watch_store *watch_store; struct pollfd *fds; #endif sd_bus *bus; diff --git a/include/subd.h b/include/subd.h index 33c997b..e9c1fab 100644 --- a/include/subd.h +++ b/include/subd.h @@ -14,7 +14,7 @@ struct pollfd; * If you add non-watch file descriptors, make sure to set their corresponding * watch to NULL, so that #subd_process_watches knows to skip them. */ -struct subd_watches { +struct subd_watch_store { struct pollfd *fds; DBusWatch **watches; int capacity; @@ -28,14 +28,14 @@ struct subd_watches { * remove and toggle functions that will handle automatic file descriptor * additions/removals. */ -struct subd_watches *subd_init_watches(DBusConnection *conn, struct pollfd *fds, - int size, DBusError *error); +struct subd_watch_store *subd_init_watches(DBusConnection *conn, + struct pollfd *fds, int size, DBusError *err); /** * This function should be called from the event loop after a successful poll to * handle the DBus watches that need to be handled (= the watches whose file * descriptor returned an event). */ -void subd_process_watches(DBusConnection *conn, struct subd_watches *watches); +void subd_process_watches(DBusConnection *conn, struct subd_watch_store *watch_store); #endif From 00a82df1c2b3b7a1799a35b8a213c5f5bcf23238 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Wed, 31 Oct 2018 20:31:35 +0100 Subject: [PATCH 16/17] Increase POSIX version in subd-sdbus.c For some reason GLibc hides strdup with 200112L despite it being part of IEEE Std. 1003.1-2001. --- dbus/subd-sdbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbus/subd-sdbus.c b/dbus/subd-sdbus.c index 012f3e3..1093508 100644 --- a/dbus/subd-sdbus.c +++ b/dbus/subd-sdbus.c @@ -1,4 +1,4 @@ -#define _POSIX_C_SOURCE 200112L +#define _POSIX_C_SOURCE 200809L #include #include #include From d64815766f33072fa4bdb9f981a54c9ca0100b9b Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 2 Nov 2018 13:53:48 +0100 Subject: [PATCH 17/17] Fix Meson errors on Linux --- meson.build | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 5ccbea2..e13ffcd 100644 --- a/meson.build +++ b/meson.build @@ -19,8 +19,8 @@ mako_inc = include_directories('include') cairo = dependency('cairo') pango = dependency('pango') pangocairo = dependency('pangocairo') -sdbus = dependency(get_option('sdbus-provider'), required: get_option('sdbus')) -dbus1 = dependency('dbus-1', required: not sdbus.found()) +sdbus = dependency('lib' + get_option('sdbus-provider'), required: get_option('sdbus')) +threads = dependency('threads') wayland_client = dependency('wayland-client') wayland_protos = dependency('wayland-protocols', version: '>=1.14') @@ -29,9 +29,9 @@ mako_files = [] if sdbus.found() mako_deps += sdbus - add_project_arguments('-DHAVE_' + get_option('logind-provider').to_upper(), language: 'c') + add_project_arguments('-DHAVE_' + get_option('sdbus-provider').to_upper(), language: 'c') else - mako_deps += dbus1 + mako_deps += dependency('dbus-1') mako_files += [ 'dbus/subd-vtable.c', 'dbus/subd-watch.c', @@ -45,9 +45,9 @@ subdir('protocol') mako_deps += [ cairo, client_protos, - sdbus, pango, pangocairo, + threads, wayland_client, ]