diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 55 | ||||
-rw-r--r-- | rcm-server.c | 729 | ||||
-rw-r--r-- | utils.h | 104 |
4 files changed, 890 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f9d16a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +rcm-server diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c514d8d --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +# +# Copyright (C) 2015 David Härdeman <david@hardeman.nu> +# + +# +# Generic settings +# +CC = gcc +GENERIC_CFLAGS = -g -Wall -Werror -D_FILE_OFFSET_BITS=64 \ + -D_LARGEFILE_SOURCE=1 -D_LARGEFILE64_SOURCE=1 -flto +EXTRA_CFLAGS = +GENERIC_LDFLAGS = +EXTRA_LDFLAGS = +RCM_PACKAGES = libudev libsystemd +RCM_CFLAGS = ${GENERIC_CFLAGS} ${EXTRA_CFLAGS} $(shell pkg-config --cflags ${RCM_PACKAGES}) +RCM_LDFLAGS = ${GENERIC_LDFLAGS} ${EXTRA_LDFLAGS} $(shell pkg-config --libs ${RCM_PACKAGES}) +INSTALL = install -c +INSTALL_PROGRAM = ${INSTALL} +INSTALL_DATA = ${INSTALL} -m 644 +RCM_COMPILE = $(CC) $(RCM_CFLAGS) +RCM_LINK = $(CC) $(RCM_CFLAGS) $(RCM_LDFLAGS) +RCM_SERVER_OBJ = rcm-server.o +RCM_SERVER_HDR = utils.h + +DESTDIR ?= +prefix = /usr +usrbindir = ${prefix}/bin +mandir = ${prefix}/share/man + +.SUFFIXES: + +# +# Targets +# + +all: rcm-server +.DEFAULT: all + +%.o: %.c $(RCM_SERVER_HDR) + $(RCM_COMPILE) -o $@ -c $< + +rcm-server: $(RCM_SERVER_OBJ) + $(RCM_LINK) -o $@ $^ + +install: all + $(INSTALL_PROGRAM) -D rcm-server $(DESTDIR)$(usrbindir)/rcm-server + +uninstall: + - rm -f $(DESTDIR)$(usrbindir)/rcm-server + +clean: + - rm -f *.o rcm-server + +.PHONY: install uninstall clean all + diff --git a/rcm-server.c b/rcm-server.c new file mode 100644 index 0000000..fb5eba9 --- /dev/null +++ b/rcm-server.c @@ -0,0 +1,729 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <libudev.h> +#include <unistd.h> +#include <string.h> +#include <systemd/sd-bus.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> + +#include "utils.h" + +struct keycode { + char *name; + char *value; +}; + +struct keymap { + char *name; + uint16_t rows; + uint16_t cols; + struct list_head list; + struct keycode keycodes[]; +}; + +struct device { + char *name; + char *path; + char *driver_name; + char *keymap_name; + struct list_head list; + struct list_head keymaps; +}; + +struct manager { + sd_bus *bus; + sd_event *event; + struct udev *udev; + struct udev_monitor *udev_mon; + sd_event_source *udev_ev; + struct list_head devices; + unsigned num_devices; +}; + +static int +method_echostring(sd_bus_message *m, void *userdata, sd_bus_error *error) +{ + const char *msg; + int r; + + r = sd_bus_message_read(m, "s", &msg); + if (r < 0) { + fprintf(stderr, "Failed to parse parameters: %s\n", strerror(-r)); + return sd_bus_error_set_errno(error, r); + } + + return sd_bus_reply_method_return(m, "s", msg); +} + +static struct device * +find_device_by_path(struct manager *mgr, const char *path) +{ + struct device *dev; + + list_for_each_entry(dev, &mgr->devices, list) + if (!strcmp(dev->path, path)) + return dev; + + return NULL; +} + +static int +property_get_driver_name(sd_bus *bus, const char *path, const char *interface, + const char *property, sd_bus_message *reply, void *userdata, + sd_bus_error *error) +{ + struct manager *mgr = userdata; + struct device *dev; + + dev = find_device_by_path(mgr, path); + if (!dev) { + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidDevice", "Sorry, invalid device"); + return -EINVAL; + } + + if (!strcmp(property, "DriverName")) + return sd_bus_message_append(reply, "s", dev->driver_name); + else if (!strcmp(property, "KernelKeymapName")) + return sd_bus_message_append(reply, "s", dev->keymap_name); + + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidProperty", "Sorry, invalid property"); + return -EINVAL; +} + + +static int +method_listkeymaps(sd_bus_message *m, void *userdata, sd_bus_error *error) +{ + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + struct manager *mgr = userdata; + struct device *dev; + struct keymap *keymap; + int r; + + dev = find_device_by_path(mgr, sd_bus_message_get_path(m)); + if (!dev) { + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidDevice", "Sorry, invalid device"); + return -EINVAL; + } + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + goto out; + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + goto out; + + list_for_each_entry(keymap, &dev->keymaps, list) { + printf("Listing keymaps for %s: %s\n", dev->name, keymap->name); + r = sd_bus_message_append(reply, "s", keymap->name); + if (r < 0) + goto out; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto out; + + r = sd_bus_send(NULL, reply, NULL); + if (r < 0) + goto out; + + return 1; + +out: + return sd_bus_error_set_errno(error, r); +} + +static struct keymap * +find_keymap_by_name(struct device *dev, const char *name) +{ + struct keymap *keymap; + + list_for_each_entry(keymap, &dev->keymaps, list) + if (!strcmp(keymap->name, name)) + return keymap; + + return NULL; +} + +static int +method_getkeymap(sd_bus_message *m, void *userdata, sd_bus_error *error) +{ + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + struct manager *mgr = userdata; + struct device *dev; + struct keymap *keymap; + const char *name; + int r; + unsigned i; + + dev = find_device_by_path(mgr, sd_bus_message_get_path(m)); + if (!dev) { + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidDevice", "Sorry, invalid device"); + return -EINVAL; + } + + r = sd_bus_message_read(m, "s", &name); + if (r < 0) { + fprintf(stderr, "Failed to parse parameters: %s\n", strerror(-r)); + return r; + } + + keymap = find_keymap_by_name(dev, name); + if (!keymap) { + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidKeymap", "Sorry, unknown keymap"); + return -EINVAL; + } + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + goto out; + + r = sd_bus_message_append(reply, "q", keymap->cols); + if (r < 0) + goto out; + + r = sd_bus_message_append(reply, "q", keymap->rows); + if (r < 0) + goto out; + + r = sd_bus_message_open_container(reply, 'a', "a{sv}"); + if (r < 0) + goto out; + + for (i = 0; i < (keymap->rows * keymap->cols); i++) { + r = sd_bus_message_append(reply, "a{sv}", 2, + "name", "s", keymap->keycodes[i].name, + "keycode", "s", keymap->keycodes[i].value); + if (r < 0) + goto out; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto out; + + r = sd_bus_send(NULL, reply, NULL); + if (r < 0) + goto out; + + return 1; + +out: + return sd_bus_error_set_errno(error, r); +} + +static const sd_bus_vtable device_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("DriverName", "s", property_get_driver_name, 0, 0), + SD_BUS_PROPERTY("KernelKeymapName", "s", property_get_driver_name, 0, 0), + SD_BUS_METHOD("EchoString", "s", "s", method_echostring, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListKeymaps", NULL, "as", method_listkeymaps, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetKeymap", "s", "qqaa{sv}", method_getkeymap, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +static int +my_bus_emit_object_changed(sd_bus *bus, const char *path, bool added) +{ + int r; + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + + printf("Sending signal %s for path %s\n", + added ? "InterfacesAdded" : "InterfacesRemoved", + path); + + r = sd_bus_message_new_signal(bus, &m, "/org/gnome/RemoteControlManager", + "org.freedesktop.DBus.ObjectManager", + added ? "InterfacesAdded" : "InterfacesRemoved"); + if (r < 0) + return r; + + r = sd_bus_message_append_basic(m, 'o', path); + if (r < 0) + return r; + + if (added) { + r = sd_bus_message_open_container(m, 'a', "{sa{sv}}"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "{sa{sv}}", "org.gnome.RemoteControlManager.Device", 0); + if (r < 0) + return r; + } else { + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", "org.gnome.RemoteControlManager.Device"); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_send(bus, m, NULL); + return r; +} + +static void +remove_device(struct manager *mgr, struct udev_device *udev) +{ + const char *name; + struct device *device; + + name = udev_device_get_sysname(udev); + printf("Asked to remove device %s\n", name); + + list_for_each_entry(device, &mgr->devices, list) { + if (strcmp(device->name, name)) + continue; + list_del(&device->list); + my_bus_emit_object_changed(mgr->bus, device->path, false); + mgr->num_devices--; + break; + } +} + +static struct keymap * +keymap_read(int dfd, const char *name) +{ + int fd; + struct keymap *keymap; + unsigned i; + + if (dfd < 0 || !name) + return NULL; + + fd = openat(dfd, name, O_RDONLY); + if (fd < 0) + return NULL; + + keymap = zmalloc(sizeof(*keymap) + (8 * 3) * sizeof(struct keycode)); + if (!keymap) { + close(fd); + return NULL; + } + + keymap->name = strdup(name); + if (!keymap->name) { + close(fd); + free(keymap); + return NULL; + } + + keymap->cols = 3; + keymap->rows = 8; + + for (i = 0; i < (keymap->cols * keymap->rows); i++) { + keymap->keycodes[i].name = "key-1-2"; + keymap->keycodes[i].value = "KEY_COFFEE"; + } + /* FIXME: Actually read the keymap :) */ + + close(fd); + return keymap; +} + +static int +keymaps_load_dir(struct device *device, const char *path) +{ + DIR *dir; + int dfd; + struct dirent *dent; + struct keymap *keymap; + + dir = opendir(path); + if (!dir) + return -1; + + dfd = dirfd(dir); + if (dfd < 0) { + closedir(dir); + return -1; + } + + while ((dent = readdir(dir))) { + switch (dent->d_type) { + case DT_REG: + case DT_LNK: + case DT_UNKNOWN: + keymap = keymap_read(dfd, dent->d_name); + if (keymap) + list_add(&keymap->list, &device->keymaps); + break; + default: + break; + } + } + + if (closedir(dir) < 0) + return -1; + + return 0; +} + +static int +keymaps_load(struct device *device) +{ + char pdpath[strlen("./keymaps/per-device/") + strlen(device->name) + 1]; + + sprintf(pdpath, "./keymaps/per-device/%s", device->name); + + printf("Loading per-device keymaps...\n"); + keymaps_load_dir(device, pdpath); + printf("Loading default keymaps...\n"); + keymaps_load_dir(device, "./keymaps/default"); + + return 0; +} + +static void +add_device(struct manager *mgr, struct udev_device *udev) +{ + const char *name; + const char *str; + char *path; + struct device *device; + + name = udev_device_get_sysname(udev); + if (asprintf(&path, "/org/gnome/RemoteControlManager/%s", name) < 0) { + fprintf(stderr, "asprintf failed: %m\n"); + return; + } + + device = malloc(sizeof(*device)); + if (!device) { + fprintf(stderr, "malloc failed: %m\n"); + free(path); + return; + } + + list_init(&device->keymaps); + device->path = path; + device->name = device->path + strlen("/org/gnome/RemoteControlManager/"); + + if (keymaps_load(device) < 0) { + fprintf(stderr, "failed to load keymaps: %m\n"); + free(path); + free(device); + } + + str = udev_device_get_sysattr_value(udev, "uevent"); + if (str) { + char tmp[strlen(str) + 1]; + char *token; + + strcpy(tmp, str); + + for (token = strtok(tmp, "\n"); token; token = strtok(NULL, "\n")) { + if (!strncmp(token, "DRV_NAME=", strlen("DRV_NAME="))) + device->driver_name = strdup(token + strlen("DRV_NAME=")); + else if (!strncmp(token, "NAME=", strlen("NAME="))) + device->keymap_name = strdup(token + strlen("NAME=")); + else + printf("Unused uevent: %s\n", token); + } + } + + printf("Adding Device Object\n"); + printf(" Path : %s\n", udev_device_get_syspath(udev)); + printf(" Node : %s\n", udev_device_get_devnode(udev)); + printf(" Subsystem : %s\n", udev_device_get_subsystem(udev)); + printf(" Devtype : %s\n", udev_device_get_devtype(udev)); + printf(" Action : %s\n", udev_device_get_action(udev)); + printf(" Protocols : %s\n", + udev_device_get_sysattr_value(udev, "protocols")); + printf(" Uevent : %s\n", + udev_device_get_sysattr_value(udev, "uevent")); + + list_add(&device->list, &mgr->devices); + mgr->num_devices++; + + my_bus_emit_object_changed(mgr->bus, path, true); +} + +static int +udev_read(sd_event_source *s, int fd, uint32_t revents, void *userdata) +{ + struct manager *mgr = userdata; + struct udev_device *dev; + + if (revents & EPOLLHUP) { + fprintf(stderr, "udev connection closed!\n"); + return 0; + } + + if (!(revents & EPOLLIN)) { + fprintf(stderr, "unexpected udev event: %" PRIu32 "\n", revents); + return 0; + } + + while ((dev = udev_monitor_receive_device(mgr->udev_mon))) { + printf("Read device: %s\n", udev_device_get_syspath(dev)); + if (!strcmp(udev_device_get_action(dev), "add")) + add_device(mgr, dev); + else if (!strcmp(udev_device_get_action(dev), "remove")) + remove_device(mgr, dev); + udev_device_unref(dev); + } + + return 1; +} + +static int +udev_setup(struct manager *mgr) +{ + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + struct udev_device *dev; + + mgr->udev = udev_new(); + if (!mgr->udev) + return -ENOMEM; + + mgr->udev_mon = udev_monitor_new_from_netlink(mgr->udev, "udev"); + udev_monitor_filter_add_match_subsystem_devtype(mgr->udev_mon, "rc", NULL); + udev_monitor_enable_receiving(mgr->udev_mon); + + enumerate = udev_enumerate_new(mgr->udev); + udev_enumerate_add_match_subsystem(enumerate, "rc"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + + udev_list_entry_foreach(dev_list_entry, devices) { + const char *path; + + path = udev_list_entry_get_name(dev_list_entry); + if (!path) { + printf("Failed to get udev name\n"); + continue; + } + + dev = udev_device_new_from_syspath(mgr->udev, path); + if (!dev) { + printf("Failed to create udev device\n"); + continue; + } + + add_device(mgr, dev); + + udev_device_unref(dev); + } + udev_enumerate_unref(enumerate); + + return sd_event_add_io(mgr->event, &mgr->udev_ev, + udev_monitor_get_fd(mgr->udev_mon), + EPOLLIN, udev_read, mgr); +} + +static int +enumerator(sd_bus *bus, const char *path, void *userdata, char ***retnodes, sd_bus_error *error) +{ + _cleanup_strv_free_ char **nodes = NULL; + struct manager *mgr = userdata; + struct device *device; + int i = 0; + + if (!path) + return 0; + + nodes = zmalloc((mgr->num_devices + 1) * sizeof(char *)); + if (!nodes) + return -ENOMEM; + + list_for_each_entry(device, &mgr->devices, list) { + nodes[i] = strdup(device->path); + if (!nodes[i]) + return -ENOMEM; + i++; + } + + nodes[i++] = NULL; + *retnodes = nodes; + nodes = NULL; + + return 1; +} + +static void +free_manager(struct manager *mgr) { + if (!mgr) + return; + + sd_event_source_unref(mgr->udev_ev); + sd_bus_detach_event(mgr->bus); + sd_event_unref(mgr->event); + udev_monitor_unref(mgr->udev_mon); + udev_unref(mgr->udev); + + while (!list_empty(&mgr->devices)) { + struct device *dev = list_first_entry(&mgr->devices, typeof(*dev), list); + list_del(&dev->list); + free(dev->path); + free(dev->driver_name); + free(dev->keymap_name); + + while (!list_empty(&dev->keymaps)) { + struct keymap *keymap = list_first_entry(&dev->keymaps, typeof(*keymap), list); + list_del(&keymap->list); + free(keymap->name); + free(keymap); + } + + free(dev); + } + free(mgr); +} + +static int +block_signals(void) +{ + sigset_t sigset; + int r; + + r = sigemptyset(&sigset); + if (r < 0) + return r; + + r = sigaddset(&sigset, SIGINT); + if (r < 0) + return r; + + r = sigaddset(&sigset, SIGTERM); + if (r < 0) + return r; + + return sigprocmask(SIG_BLOCK, &sigset, NULL); +} + +int +main(int argc, char **argv) +{ + int r; + struct manager *mgr; + _cleanup_bus_close_unref_ sd_bus *bus = NULL; + _cleanup_bus_slot_unref_ struct sd_bus_slot *vtable_slot = NULL; + _cleanup_bus_slot_unref_ struct sd_bus_slot *enumerator_slot = NULL; + _cleanup_bus_slot_unref_ struct sd_bus_slot *objm_slot = NULL; + _cleanup_event_source_unref_ sd_event_source *sigint_ev = NULL; + _cleanup_event_source_unref_ sd_event_source *sigterm_ev = NULL; + + mgr = zmalloc(sizeof(*mgr)); + if (!mgr) { + fprintf(stderr, "Failed to allocate memory: %m\n"); + goto finish; + } + list_init(&mgr->devices); + + r = sd_bus_open_user(&bus); + if (r < 0) { + fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-r)); + goto finish; + } + mgr->bus = bus; + + r = sd_bus_request_name(mgr->bus, "org.gnome.RemoteControlManager", 0); + if (r < 0) { + fprintf(stderr, "Failed to acquire service name: %s\n", strerror(-r)); + goto finish; + } + + r = sd_bus_add_fallback_vtable(mgr->bus, &vtable_slot, "/org/gnome/RemoteControlManager", + "org.gnome.RemoteControlManager.Device", + device_vtable, NULL, mgr); + if (r < 0) { + fprintf(stderr, "Failed to add vtable: %s\n", strerror(-r)); + goto finish; + } + + r = sd_bus_add_node_enumerator(mgr->bus, &enumerator_slot, "/org/gnome/RemoteControlManager", enumerator, mgr); + if (r < 0) { + fprintf(stderr, "Failed to create node enumerator: %s\n", strerror(-r)); + goto finish; + } + + r = sd_bus_add_object_manager(mgr->bus, &objm_slot, "/org/gnome/RemoteControlManager"); + if (r < 0) { + fprintf(stderr, "Failed to create object manager: %s\n", strerror(-r)); + goto finish; + } + + r = sd_event_default(&mgr->event); + if (r < 0) { + fprintf(stderr, "Failed to create sd_event: %s\n", strerror(-r)); + goto finish; + } + + r = sd_bus_attach_event(mgr->bus, mgr->event, 0); + if (r < 0) { + fprintf(stderr, "Failed to attach bus to event loop: %s\n", strerror(-r)); + goto finish; + } + + r = udev_setup(mgr); + if (r < 0) { + fprintf(stderr, "Failed to setup udev monitoring: %s\n", strerror(-r)); + goto finish; + } + + r = block_signals(); + if (r < 0) { + fprintf(stderr, "Failed to block signals: %m\n"); + goto finish; + } + + sd_event_add_signal(mgr->event, &sigint_ev, SIGINT, NULL, NULL); + sd_event_add_signal(mgr->event, &sigterm_ev, SIGTERM, NULL, NULL); + + r = sd_event_loop(mgr->event); + if (r < 0) { + fprintf(stderr, "Event loop failed: %s\n", strerror(-r)); + goto finish; + } + + sd_event_get_exit_code(mgr->event, &r); + +finish: + free_manager(mgr); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} + +#if 0 +static gboolean +on_irrx(RCIRDevice *object, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GVariantBuilder builder; + GVariant *ret; + + printf("In on_irrx\n"); + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, "{sv}", "name", + g_variant_new_string("apan")); + g_variant_builder_add(&builder, "{sv}", "foo", + g_variant_new_string("bar")); + + ret = g_variant_builder_end (&builder); + + rcirdevice_complete_get_irrxparameters(object, + invocation, + ret); + + return true; +} + +#endif + @@ -0,0 +1,104 @@ + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; + +static inline void list_init(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void list_del(struct list_head *list) +{ + list->next->prev = list->prev; + list->prev->next = list->next; +} + +static inline void list_add(struct list_head *new, struct list_head *list) +{ + list->next->prev = new; + new->next = list->next; + new->prev = list; + list->next = new; +} + +static inline bool list_empty(struct list_head *list) +{ + return list->next == list; +} + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +static inline void* zmalloc(size_t size) +{ + return calloc(1, size); +} + +#define _cleanup_(x) __attribute__((cleanup(x))) + +#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ + static inline void func##p(type *p) { \ + if (*p) \ + func(*p); \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + + +static inline void sd_bus_close_unrefp(sd_bus **bus) { + if (*bus) { + sd_bus_flush(*bus); + sd_bus_close(*bus); + sd_bus_unref(*bus); + } +} +#define _cleanup_bus_close_unref_ _cleanup_(sd_bus_close_unrefp) + +static inline void strv_free(char **l) { + char **k; + if (l) { + for (k = l; *k; k++) + free(k); + free(l); + } +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char **, strv_free); +#define _cleanup_strv_free_ _cleanup_(strv_freep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(void *, free); +#define _cleanup_free_ _cleanup_(freep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus *, sd_bus_unref); +#define _cleanup_bus_unref_ _cleanup_(sd_bus_unrefp) + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_slot *, sd_bus_slot_unref); +#define _cleanup_bus_slot_unref_ _cleanup_(sd_bus_slot_unrefp) + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_message *, sd_bus_message_unref); +#define _cleanup_bus_message_unref_ _cleanup_(sd_bus_message_unrefp) + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source *, sd_event_source_unref); +#define _cleanup_event_source_unref_ _cleanup_(sd_event_source_unrefp) + |