#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" #include "rcm-server-kdb.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, "SysName")) return sd_bus_message_append(reply, "s", dev->name); else if (!strcmp(property, "Description")) return sd_bus_message_append(reply, "s", dev->input_name); else 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"); else if (!strcmp(property, "Error")) return sd_bus_message_append(reply, "s", dev->error); sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidProperty", "Sorry, invalid property"); return -EINVAL; } static int sdbus_get_psk_triplet(sd_bus_message *m, unsigned *ret_protocol, uint64_t *ret_scancode, struct linux_input_keycode **ret_keycode) { const char *protocol = NULL; unsigned protocol_numeric; uint64_t scancode; bool scancode_found = false; struct linux_input_keycode *lik = NULL; const char *keycode = NULL; unsigned i; int r; r = sd_bus_message_enter_container(m, 'a', "{sv}"); if (r < 1) return r; 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 (keycode) { printf("Keycode specified more than once\n"); return -EINVAL; } r = sd_bus_message_read_basic(m, type, &keycode); if (r < 0) return r; if (strcmp(keycode, "KEY_RESERVED")) { 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; r = sd_bus_message_exit_container(m); if (r < 0) return r; if (!protocol) { printf("Protocol missing\n"); return -EINVAL; } if (!scancode_found) { printf("Scancode missing\n"); return -EINVAL; } if (!keycode) { printf("Keycode missing\n"); return -EINVAL; } *ret_protocol = protocol_numeric; *ret_scancode = scancode; *ret_keycode = lik; return 1; } static int method_getkernelmappings(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; unsigned protocol_numeric; uint64_t scancode; struct linux_input_keycode *lik; unsigned i = 0; 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; } if (dev->evdev_fd < 0) { sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.EvdevError", "Evdev device not available"); r = -EINVAL; goto out; } r = sd_bus_message_new_method_return(m, &reply); if (r < 0) goto out; r = sd_bus_message_open_container(reply, 'a', "a{sv}"); if (r < 0) goto out; while ((r = evdev_get_mapping(dev, i, &protocol_numeric, &scancode, &lik)) > 0) { r = sd_bus_message_append(reply, "a{sv}", 3, "protocol", "s", rc_protocols[protocol_numeric], "scancode", "t", scancode, "keycode", "s", lik->name); if (r < 0) goto out; i++; } 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_setkernelmappings(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; unsigned protocol; uint64_t scancode; struct linux_input_keycode *keycode; 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"); r = -EINVAL; goto out; } if (dev->evdev_fd < 0) { sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.EvdevError", "Evdev device not available"); r = -EINVAL; goto out; } r = sd_bus_message_enter_container(m, 'a', "a{sv}"); if (r < 0) goto out; while ((r = sdbus_get_psk_triplet(m, &protocol, &scancode, &keycode)) > 0) { printf("%s kernel mapping %s:0x%08lx:%s\n", keycode ? "Setting" : "Unsetting", rc_protocols[protocol], scancode, keycode ? keycode->name : ""); evdev_set_mapping(dev, protocol, scancode, keycode); } if (r < 0) goto out; r = sd_bus_message_exit_container(m); if (r < 0) goto out; return sd_bus_reply_method_return(m, NULL); out: return sd_bus_error_set_errno(error, r); } 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; unsigned protocol; uint64_t scancode; struct linux_input_keycode *lik; 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 = sdbus_get_psk_triplet(m, &protocol, &scancode, &lik)) > 0) { if (!lik) { printf("Keycode entry without keycode\n"); return -EINVAL; } if (keymap) { keymap->keycodes[keycode_count].protocol = protocol; keymap->keycodes[keycode_count].scancode = scancode; keymap->keycodes[keycode_count].lik = lik; } keycode_count++; } 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 void lirc_write(int fd, const uint32_t *v, unsigned count) { unsigned nv[count]; unsigned i; size_t l = count * sizeof(uint32_t); for (i = 0; i < count; i++) nv[i] = v[i]; printf("Write to lirc fd of %zu bytes returned %zi errno is %i aka %s\n", l, write(fd, nv, l), errno, strerror(errno)); } static int method_transmit(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; const void *v; size_t l; int r; const uint32_t *values; size_t count; printf("Transmit method called\n"); 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_array(m, SD_BUS_TYPE_UINT32, &v, &l); if (r < 0) { goto out; } else if (r == 0 || l % sizeof(uint32_t) != 0) { r = -EBADMSG; goto out; } values = v; count = l / sizeof(uint32_t); lirc_write(dev->lirc_fd, values, count); return sd_bus_reply_method_return(m, NULL); out: return sd_bus_error_set_errno(error, r); } static const sd_bus_vtable device_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("SysName", "s", property_get, 0, 0), SD_BUS_PROPERTY("Description", "s", property_get, 0, 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_PROPERTY("Error", "s", property_get, 0, 0), SD_BUS_METHOD("GetKernelMappings", NULL, "aa{sv}", method_getkernelmappings, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetKernelMappings", "aa{sv}", NULL, method_setkernelmappings, SD_BUS_VTABLE_UNPRIVILEGED), 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_METHOD("Transmit", "au", NULL, method_transmit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("KeyPressed", "sts", 0), SD_BUS_SIGNAL("KeyReleased", "s", 0), SD_BUS_SIGNAL("Event", "s", 0), SD_BUS_VTABLE_END }; 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); sd_bus_emit_object_removed(mgr->bus, device->path); mgr->num_devices--; break; } } static int lirc_read(sd_event_source *s, int fd, uint32_t revents, void *userdata) { struct device *device = userdata; uint8_t buf[100]; ssize_t bytes_read; if (fd != device->lirc_fd) fprintf(stderr, "lirc fd mismatch: %i != %i\n", device->lirc_fd, fd); if (revents & EPOLLHUP) { fprintf(stderr, "lirc connection closed!\n"); close(fd); device->lirc_fd = -1; return 0; } if (!(revents & EPOLLIN)) { fprintf(stderr, "unexpected lirc event: %" PRIu32 "\n", revents); return 0; } while ((bytes_read = read(fd, buf, sizeof(buf))) > 0) printf("Read %zi bytes from lirc dev\n", bytes_read); return 0; } static int lirc_setup(struct device *device, const char *path) { if (!device) return -EINVAL; if (device->lirc_fd >= 0) { printf("Multiple lirc devices!?\n"); return 0; } device->lirc_fd = open(path, O_RDWR | O_NONBLOCK); if (device->lirc_fd < 0) { printf("Failed to open lirc device %s: %s\n", path, strerror(errno)); return -errno; } if (sd_event_add_io(device->mgr->event, &device->lirc_ev, device->lirc_fd, EPOLLIN, lirc_read, device) < 0) { printf("Failed to add event source for lirc device %s: %s\n", path, strerror(errno)); close(device->lirc_fd); device->lirc_fd = -1; return -errno; } 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; int r; 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; device->lirc_fd = -1; device->error = NULL; device->input_name = NULL; 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_add_match_sysname(enumerate, "lirc*"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { const char *path; struct udev_device *udev_dev; const char *devnode; const char *subsys; path = udev_list_entry_get_name(dev_list_entry); if (!path) { printf("Failed to get udev name\n"); continue; } udev_dev = udev_device_new_from_syspath(mgr->udev, path); if (!udev_dev) { printf("Failed to create udev device\n"); continue; } devnode = udev_device_get_devnode(udev_dev); if (!devnode) { printf("Failed to determine udev_dev devnode\n"); goto next; } subsys = udev_device_get_subsystem(udev_dev); if (!subsys) { printf("Failed to determine udev_dev subsystem\n"); goto next; } if (!strcmp(subsys, "input")) { r = evdev_setup(device, devnode); if (r < 0) { printf("Failed to setup evdev: %s\n", devnode); device->error = "Error: Failed to setup evdev"; goto next; } } else if (!strcmp(subsys, "lirc")) { r = lirc_setup(device, devnode); if (r < 0) { printf("Failed to setup lirc: %s\n", devnode); device->error = "Error: Failed to setup lirc"; goto next; } } else { printf("Unknown subsystem, ignored %s\n", devnode); goto next; } next: udev_device_unref(udev_dev); } 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("\tName : %s\n", device->name); printf("\tInput name: %s\n", device->input_name); printf("\tDriver : %s\n", device->driver_name); printf("\tKernel map: %s\n", device->keymap_name); printf("\tDevnode fd: %i\n", device->evdev_fd); printf("\tLIRC fd : %i\n", device->lirc_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++; sd_bus_emit_object_added(mgr->bus, path); } 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; kdb_close(mgr); 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->input_name); 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_event_default(&mgr->event); if (r < 0) { fprintf(stderr, "Failed to create sd_event: %s\n", strerror(-r)); goto finish; } r = kdb_init(mgr, "./rcm.sqlite"); if (r < 0) { fprintf(stderr, "Failed to open/create sqlite database\n"); goto finish; } r = kdb_add_directory(mgr, "./keymaps/db"); if (r < 0) { fprintf(stderr, "Failed to add keymap directory: %s\n", strerror(-r)); goto finish; } 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_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; }