#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "utils.h" #include "rcm-server-main.h" #include "rcm-server-keymap.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", keymap->keycodes[i].protocol, "scancode", "t", keymap->keycodes[i].scancode, "keycode", "s", keymap->keycodes[i].keycode); 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]->keycode); 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(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; const char *name; const char *description; 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 = sd_bus_message_read(m, "sssqq", &id, &name, &description, &cols, &rows); if (r < 0) { fprintf(stderr, "Failed to parse parameters: %s\n", strerror(-r)); goto out; } keymap = find_keymap_by_id(dev, id); if (!keymap) return sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidKeymap", "Sorry, unknown keymap"); printf("Asked to update keymap\n"); printf("\tID : %s\n", id); printf("\tName: %s\n", name); printf("\tDesc: %s\n", description); printf("\tCols: %u\n", cols); printf("\tRows: %u\n", rows); printf("\tKeymap:\n"); r = sd_bus_message_enter_container(m, 'a', "a{sv}"); if (r < 0) goto out; while ((r = sd_bus_message_enter_container(m, 'a', "{sv}")) > 0) { const char *protocol; uint64_t scancode; const char *keycode; 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) goto out; r = sd_bus_message_peek_type(m, NULL, &contents); if (r < 0) goto out; r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); if (r < 0) goto out; r = sd_bus_message_peek_type(m, &type, &contents); if (r < 0) goto out; if (!strcmp(name, "protocol")) { if (type != SD_BUS_TYPE_STRING) { printf("Invalid type for protocol (%c)\n", type); r = -EINVAL; goto out; } r = sd_bus_message_read_basic(m, type, &protocol); if (r < 0) goto out; } else if (!strcmp(name, "scancode")) { if (type != SD_BUS_TYPE_UINT64) { printf("Invalid type for scancode (%c)\n", type); r = -EINVAL; goto out; } r = sd_bus_message_read_basic(m, type, &scancode); if (r < 0) goto out; } else if (!strcmp(name, "keycode")) { if (type != SD_BUS_TYPE_STRING) { printf("Invalid type for keycode (%c)\n", type); r = -EINVAL; goto out; } r = sd_bus_message_read_basic(m, type, &keycode); if (r < 0) goto out; } else { r = sd_bus_message_skip(reply, contents); if (r < 0) goto out; } r = sd_bus_message_exit_container(m); if (r < 0) goto out; r = sd_bus_message_exit_container(m); if (r < 0) goto out; } if (r < 0) goto out; printf("\t\t%s:%" PRIx64 ":%s\n", protocol, scancode, keycode); r = sd_bus_message_exit_container(m); if (r < 0) goto out; } if (r < 0) goto out; r = sd_bus_message_exit_container(m); if (r < 0) goto out; printf("\tLayout:\n"); r = sd_bus_message_enter_container(m, 'a', "a{sv}"); if (r < 0) goto out; while ((r = sd_bus_message_enter_container(m, 'a', "{sv}")) > 0) { bool blank = false; bool button = false; const char *keycode = 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; char type; r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name); if (r < 0) goto out; r = sd_bus_message_peek_type(m, NULL, &contents); if (r < 0) goto out; r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); if (r < 0) goto out; r = sd_bus_message_peek_type(m, &type, &contents); if (r < 0) goto out; if (!strcmp(name, "type")) { if (type != SD_BUS_TYPE_STRING) { printf("Invalid type for type (%c)\n", type); r = -EINVAL; goto out; } r = sd_bus_message_read_basic(m, type, &button_type); if (r < 0) goto out; if (!strcasecmp(button_type, "button")) button = true; else if (!strcasecmp(button_type, "blank")) blank = true; else { printf("Invalid button type: %s\n", button_type); r = -EINVAL; goto out; } } else if (!strcmp(name, "keycode")) { if (type != SD_BUS_TYPE_STRING) { printf("Invalid type for keycode (%c)\n", type); r = -EINVAL; goto out; } r = sd_bus_message_read_basic(m, type, &keycode); if (r < 0) goto out; } else { r = sd_bus_message_skip(reply, contents); if (r < 0) goto out; } r = sd_bus_message_exit_container(m); if (r < 0) goto out; r = sd_bus_message_exit_container(m); if (r < 0) goto out; } if (r < 0) goto out; if ((!blank && !button) || (blank && button) || (button && !keycode)) { printf("Invalid layout specification\n"); r = -EINVAL; goto out; } printf("\t\tButton: %s\n", button ? keycode : "blank"); r = sd_bus_message_exit_container(m); if (r < 0) goto out; } if (r < 0) goto out; r = sd_bus_message_exit_container(m); if (r < 0) goto out; /* FIXME: save the result */ 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("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", "s", 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 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("\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("\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); } #include typedef uint64_t usec_t; #define USEC_PER_SEC ((usec_t)1000000ULL) #define NSEC_PER_USEC ((usec_t)1000ULL) static usec_t now(clockid_t clk_id) { struct timespec ts; clock_gettime(clk_id, &ts); return (usec_t)(ts.tv_sec * USEC_PER_SEC) + (usec_t)(ts.tv_nsec / NSEC_PER_USEC); } static int timeout_cb(sd_event_source *source, usec_t usec, void *userdata) { struct manager *mgr = userdata; static bool pressed = true; unsigned timeout; sd_bus_emit_signal(mgr->bus, "/org/gnome/RemoteControlManager/rc0", "org.gnome.RemoteControlManager.Device", pressed ? "KeyPressed" : "KeyReleased", "s", "KEY_OK"); if (pressed) timeout = USEC_PER_SEC / 2; else timeout = USEC_PER_SEC * 3; sd_event_source_set_time(source, now(CLOCK_MONOTONIC) + timeout); sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); pressed = !pressed; return 0; } 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; _cleanup_event_source_unref_ sd_event_source *timeout_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); printf("Sending fake keypress events\n"); sd_event_add_time(mgr->event, &timeout_ev, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 3 * USEC_PER_SEC, 0, timeout_cb, mgr); 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; }