#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #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