#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "shared.h" #include "rcm-server-main.h" #include "rcm-server-keymap.h" #include "rcm-server-evdev.h" 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(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); else if (!strcmp(property, "HardwareType")) return sd_bus_message_append(reply, "s", "ir"); 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->id); 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 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 *id; 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", &id); if (r < 0) { fprintf(stderr, "Failed to parse parameters: %s\n", strerror(-r)); return r; } keymap = find_keymap_by_id(dev, id); 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, "s", keymap->name); if (r < 0) goto out; r = sd_bus_message_append(reply, "s", keymap->description); 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; /* Keymap */ r = sd_bus_message_open_container(reply, 'a', "a{sv}"); if (r < 0) goto out; for (i = 0; i < keymap->keycode_count; i++) { r = sd_bus_message_append(reply, "a{sv}", 3, "protocol", "s", rc_protocols[keymap->keycodes[i].protocol], "scancode", "t", keymap->keycodes[i].scancode, "keycode", "s", keymap->keycodes[i].lik->name); if (r < 0) goto out; } r = sd_bus_message_close_container(reply); if (r < 0) goto out; /* Layout */ r = sd_bus_message_open_container(reply, 'a', "a{sv}"); if (r < 0) goto out; for (i = 0; i < (keymap->rows * keymap->cols); i++) { if (!keymap->layout[i]) r = sd_bus_message_append(reply, "a{sv}", 1, "type", "s", "blank"); else r = sd_bus_message_append(reply, "a{sv}", 2, "type", "s", "button", "keycode", "s", keymap->layout[i]->lik->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 int method_setkeymap_parse_dbus_msg(sd_bus_message *m, struct keymap *keymap, const char **ret_id, unsigned *ret_keycode_count, uint16_t *ret_cols, uint16_t *ret_rows) { const char *id; const char *name; const char *description; uint16_t cols, rows; unsigned keycode_count = 0; unsigned layout_count = 0; unsigned i; int r; r = sd_bus_message_rewind(m, 1); if (r < 0) return r; r = sd_bus_message_read(m, "sssqq", &id, &name, &description, &cols, &rows); if (r < 0) { fprintf(stderr, "Failed to parse parameters: %s\n", strerror(-r)); return r; } if (!id) { printf("Id missing\n"); return -EINVAL; } if (!name) { printf("Name missing\n"); return -EINVAL; } if (!description) { printf("Description missing\n"); return -EINVAL; } if (cols < 1 || cols > REMOTE_LAYOUT_MAX_WIDTH) { printf("Invalid column size\n"); return -EINVAL; } if (rows < 1 || rows > REMOTE_LAYOUT_MAX_HEIGHT) { printf("Invalid row size\n"); return -EINVAL; } if (keymap) { keymap->id = strdup(id); keymap->name = strdup(name); keymap->description = strdup(description); keymap->cols = cols; keymap->rows = rows; if (!keymap->id || !keymap->name || !keymap->description) return -ENOMEM; } r = sd_bus_message_enter_container(m, 'a', "a{sv}"); if (r < 0) return r; while ((r = sd_bus_message_enter_container(m, 'a', "{sv}")) > 0) { const char *protocol = NULL; unsigned protocol_numeric; uint64_t scancode; bool scancode_found = false; struct linux_input_keycode *lik = NULL; const char *keycode; unsigned i; while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { const char *name; const char *contents; char type; r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name); if (r < 0) return r; r = sd_bus_message_peek_type(m, NULL, &contents); if (r < 0) return r; r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); if (r < 0) return r; r = sd_bus_message_peek_type(m, &type, &contents); if (r < 0) return r; if (!strcmp(name, "protocol")) { if (type != SD_BUS_TYPE_STRING) { printf("Invalid type for protocol (%c)\n", type); return -EINVAL; } if (protocol) { printf("Protocol specified more than once\n"); return -EINVAL; } r = sd_bus_message_read_basic(m, type, &protocol); if (r < 0) return r; for (i = 0; rc_protocols[i]; i++) { if (!strcmp(rc_protocols[i], protocol)) break; } if (!rc_protocols[i]) { printf("Invalid protocol name (%s)\n", protocol); return -EINVAL; } protocol_numeric = i; } else if (!strcmp(name, "scancode")) { if (type != SD_BUS_TYPE_UINT64) { printf("Invalid type for scancode (%c)\n", type); return -EINVAL; } if (scancode_found) { printf("Scancode specified more than once\n"); return -EINVAL; } r = sd_bus_message_read_basic(m, type, &scancode); if (r < 0) return r; scancode_found = true; } else if (!strcmp(name, "keycode")) { if (type != SD_BUS_TYPE_STRING) { printf("Invalid type for keycode (%c)\n", type); return -EINVAL; } if (lik) { printf("Keycode specified more than once\n"); return -EINVAL; } r = sd_bus_message_read_basic(m, type, &keycode); if (r < 0) return r; lik = get_linux_keycode_by_name(keycode); if (!lik) { printf("Invalid keycode name (%s)\n", keycode); return -EINVAL; } } else { r = sd_bus_message_skip(m, contents); if (r < 0) return r; } r = sd_bus_message_exit_container(m); if (r < 0) return r; r = sd_bus_message_exit_container(m); if (r < 0) return r; } if (r < 0) return r; if (!protocol) { printf("Keycode entry without protocol\n"); return -EINVAL; } if (!scancode_found) { printf("Keycode entry without scancode\n"); return -EINVAL; } if (!lik) { printf("Keycode entry without keycode\n"); return -EINVAL; } if (keymap) { keymap->keycodes[keycode_count].protocol = protocol_numeric; keymap->keycodes[keycode_count].scancode = scancode; keymap->keycodes[keycode_count].lik = lik; } keycode_count++; r = sd_bus_message_exit_container(m); if (r < 0) return r; } if (r < 0) return r; r = sd_bus_message_exit_container(m); if (r < 0) return r; r = sd_bus_message_enter_container(m, 'a', "a{sv}"); if (r < 0) return r; while ((r = sd_bus_message_enter_container(m, 'a', "{sv}")) > 0) { bool blank = false; bool button = false; struct linux_input_keycode *lik = NULL; while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { const char *name; const char *contents; const char *button_type; const char *keycode; char type; r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name); if (r < 0) return r; r = sd_bus_message_peek_type(m, NULL, &contents); if (r < 0) return r; r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); if (r < 0) return r; r = sd_bus_message_peek_type(m, &type, &contents); if (r < 0) return r; if (!strcmp(name, "type")) { if (type != SD_BUS_TYPE_STRING) { printf("Invalid type for type (%c)\n", type); return -EINVAL; } r = sd_bus_message_read_basic(m, type, &button_type); if (r < 0) return r; if (button || blank) { printf("Multiple types specified in layout\n"); return -EINVAL; } if (!strcasecmp(button_type, "button")) button = true; else if (!strcasecmp(button_type, "blank")) blank = true; else { printf("Invalid button type: %s\n", button_type); return -EINVAL; } } else if (!strcmp(name, "keycode")) { if (type != SD_BUS_TYPE_STRING) { printf("Invalid type for keycode (%c)\n", type); return -EINVAL; } if (lik) { printf("Multiple keycodes specified in layout\n"); return -EINVAL; } r = sd_bus_message_read_basic(m, type, &keycode); if (r < 0) return r; lik = get_linux_keycode_by_name(keycode); if (!lik) { printf("Invalid keycode name in layout (%s)\n", keycode); return -EINVAL; } } else { r = sd_bus_message_skip(m, contents); if (r < 0) return r; } r = sd_bus_message_exit_container(m); if (r < 0) return r; r = sd_bus_message_exit_container(m); if (r < 0) return r; } if (r < 0) return r; if ((!blank && !button) || (blank && button) || (button && !lik) || (blank && lik)) { printf("Invalid layout specification\n"); return -EINVAL; } if (keymap) { if (blank) keymap->layout[layout_count] = NULL; else { for (i = 0; i < keycode_count; i++) { if (keymap->keycodes[i].lik == lik) break; } if (i >= keycode_count) { printf("Invalid keycode in layout: '%s'\n", lik->name); return -EINVAL; } keymap->layout[layout_count] = &keymap->keycodes[i]; } } layout_count++; r = sd_bus_message_exit_container(m); if (r < 0) return r; } if (r < 0) return r; r = sd_bus_message_exit_container(m); if (r < 0) return r; if (!sd_bus_message_at_end(m, 1)) { printf("Trailing message parts\n"); return -EINVAL; } if (keycode_count < 1) { printf("No keycodes specified\n"); return -EINVAL; } if (layout_count < 1) { printf("No layout specified\n"); return -EINVAL; } if (rows * cols != layout_count) { fprintf(stderr, "Layout (%u) does not match rows x cols (%ux%u)\n", layout_count, rows, cols); return -EINVAL; } if (ret_id) *ret_id = id; if (ret_keycode_count) *ret_keycode_count = keycode_count; if (keymap) keymap->keycode_count = keycode_count; if (ret_cols) *ret_cols = cols; if (ret_rows) *ret_rows = rows; return 0; } static int method_setkeymap(sd_bus_message *m, void *userdata, sd_bus_error *error) { struct manager *mgr = userdata; struct device *dev; struct keymap *old_keymap; struct keymap *new_keymap; const char *id; unsigned keycode_count; uint16_t cols, rows; int r; dev = find_device_by_path(mgr, sd_bus_message_get_path(m)); if (!dev) return sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidDevice", "Sorry, invalid device"); r = method_setkeymap_parse_dbus_msg(m, NULL, &id, &keycode_count, &cols, &rows); if (r < 0) goto out; old_keymap = find_keymap_by_id(dev, id); if (!old_keymap) return sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidKeymap", "Sorry, unknown keymap"); printf("Asked to update keymap '%s'\n", id); new_keymap = zmalloc(sizeof(*new_keymap) + sizeof(struct keycode) * keycode_count); if (!new_keymap) { r = -ENOMEM; goto out; } new_keymap->layout = zmalloc(sizeof(struct keycode *) * cols * rows); if (!new_keymap->layout) { r = -ENOMEM; goto out; } r = method_setkeymap_parse_dbus_msg(m, new_keymap, NULL, NULL, NULL, NULL); if (r < 0) goto out; printf("\tUpdated keymap: name (%s), desc (%s), keycodes(%u), rows(%u), cols(%u)\n", new_keymap->name, new_keymap->description, new_keymap->keycode_count, new_keymap->rows, new_keymap->cols); r = keymap_write(new_keymap); if (r < 0) goto out; list_replace(&old_keymap->list, &new_keymap->list); keymap_free(old_keymap); return sd_bus_reply_method_return(m, NULL); out: keymap_free(new_keymap); 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, 0, 0), SD_BUS_PROPERTY("KernelKeymapName", "s", property_get, 0, 0), SD_BUS_PROPERTY("HardwareType", "s", property_get, 0, 0), SD_BUS_METHOD("ListKeymaps", NULL, "as", method_listkeymaps, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetKeymap", "s", "ssqqaa{sv}aa{sv}", method_getkeymap, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetKeymap", "sssqqaa{sv}aa{sv}", NULL, method_setkeymap, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("KeyPressed", "sts", 0), SD_BUS_SIGNAL("KeyReleased", "s", 0), 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 int evdev_read(sd_event_source *s, int fd, uint32_t revents, void *userdata) { struct device *device = userdata; struct input_event ev; static const char *keycode_str = NULL; static uint32_t keycode = KEY_RESERVED; static uint32_t scancode; static bool scancode_recv = false; static bool pressed = false; unsigned i; if (fd != device->evdev_fd) fprintf(stderr, "evdev fd mismatch: %i != %i\n", device->evdev_fd, fd); if (revents & EPOLLHUP) { fprintf(stderr, "evdev connection closed!\n"); close(fd); device->evdev_fd = -1; return 0; } if (!(revents & EPOLLIN)) { fprintf(stderr, "unexpected evdev event: %" PRIu32 "\n", revents); return 0; } while (read(fd, &ev, sizeof(ev)) == sizeof(ev)) { switch (ev.type) { case EV_KEY: if (keycode != KEY_RESERVED) printf("Reading from evdev - multiple keycodes?\n"); for (i = 0; linux_input_keycodes[i].name; i++) { if (linux_input_keycodes[i].value == ev.code) break; } if (!linux_input_keycodes[i].name) { printf("evdev - unknown keycode (%u)\n", ev.code); break; } keycode_str = linux_input_keycodes[i].name; keycode = ev.code; pressed = ev.value ? true : false; break; case EV_MSC: if (ev.code == MSC_SCAN) { if (scancode_recv) printf("Reading from evdev - multiple scancodes?\n"); scancode_recv = true; scancode = ev.value; } break; case EV_SYN: if (keycode != KEY_RESERVED || scancode_recv) { printf("evdev event -"); if (keycode != KEY_RESERVED) printf(" keycode %s (%u) %s", keycode_str, keycode, pressed ? "pressed" : "released"); if (scancode_recv) printf(" scancode 0x%08x", scancode); printf("\n"); /* FIXME: protocol needs kernel support */ if (pressed && scancode_recv) sd_bus_emit_signal(device->mgr->bus, device->path, "org.gnome.RemoteControlManager.Device", "KeyPressed", "sts", "nec", scancode, keycode_str); else if (!pressed && keycode != KEY_RESERVED) sd_bus_emit_signal(device->mgr->bus, device->path, "org.gnome.RemoteControlManager.Device", "KeyReleased", "s", keycode_str); } keycode = KEY_RESERVED; keycode_str = NULL; scancode_recv = false; pressed = false; break; default: break; } } return 0; } static void add_device(struct manager *mgr, struct udev_device *udev) { const char *name; const char *str; char *path; struct device *device; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *evdev; 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->mgr = mgr; device->path = path; device->name = device->path + strlen("/org/gnome/RemoteControlManager/"); device->evdev_fd = -1; if (keymaps_load(device) < 0) { fprintf(stderr, "failed to load keymaps: %m\n"); free(path); free(device); return; } enumerate = udev_enumerate_new(mgr->udev); udev_enumerate_add_match_parent(enumerate, udev); udev_enumerate_add_match_sysname(enumerate, "event*"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { const char *path; const char *devnode; path = udev_list_entry_get_name(dev_list_entry); if (!path) { printf("Failed to get udev name\n"); continue; } evdev = udev_device_new_from_syspath(mgr->udev, path); if (!evdev) { printf("Failed to create udev device\n"); continue; } devnode = udev_device_get_devnode(evdev); if (!devnode) { printf("Failed to determine evdev devnode\n"); udev_device_unref(evdev); continue; } if (device->evdev_fd >= 0) { printf("Multiple evdev devices!?\n"); udev_device_unref(evdev); continue; } device->evdev_fd = open(devnode, O_RDONLY | O_NONBLOCK); if (device->evdev_fd < 0) { printf("Failed to open evdev device %s: %s\n", devnode, strerror(errno)); udev_device_unref(evdev); continue; } evdev_setup(device); if (sd_event_add_io(mgr->event, &device->evdev_ev, device->evdev_fd, EPOLLIN, evdev_read, device) < 0) { printf("Failed to add event source for evdev device %s: %s\n", devnode, strerror(errno)); close(device->evdev_fd); device->evdev_fd = -1; udev_device_unref(evdev); continue; } udev_device_unref(evdev); } udev_enumerate_unref(enumerate); 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("\tPath : %s\n", udev_device_get_syspath(udev)); printf("\tNode : %s\n", udev_device_get_devnode(udev)); printf("\tSubsystem : %s\n", udev_device_get_subsystem(udev)); printf("\tDevtype : %s\n", udev_device_get_devtype(udev)); printf("\tAction : %s\n", udev_device_get_action(udev)); printf("\tDriver : %s\n", device->driver_name); printf("\tKernel map: %s\n", device->keymap_name); printf("\tDevnode fd: %i\n", device->evdev_fd); printf("\tDBUS path : %s\n", device->path); printf("\tProtocols : %s\n", udev_device_get_sysattr_value(udev, "protocols")); 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; }