X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fidev.c;h=989683f39a62cc2053928b547cc09a1aa65560d5;hb=a9944163fe5600bce85898dae78cd68442a6ff7c;hp=5e3080797a75d6d4c7c4a0526f2ea5ce5c277452;hpb=e202fa31fb2d60084e7b2ab7976a81c138184d40;p=elogind.git diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c index 5e3080797..989683f39 100644 --- a/src/libsystemd-terminal/idev.c +++ b/src/libsystemd-terminal/idev.c @@ -20,17 +20,20 @@ ***/ #include +#include +#include #include #include #include #include #include +#include #include "hashmap.h" #include "idev.h" #include "idev-internal.h" #include "login-shared.h" #include "macro.h" -#include "set.h" +#include "udev-util.h" #include "util.h" static void element_open(idev_element *e); @@ -271,6 +274,22 @@ static void element_disable(idev_element *e) { } } +static void element_resume(idev_element *e, int fd) { + assert(e); + assert(fd >= 0); + + if (e->vtable->resume) + e->vtable->resume(e, fd); +} + +static void element_pause(idev_element *e, const char *mode) { + assert(e); + assert(mode); + + if (e->vtable->pause) + e->vtable->pause(e, mode); +} + /* * Sessions */ @@ -332,8 +351,8 @@ static int session_add_device(idev_session *s, idev_device *d) { error: if (r < 0) - log_debug("idev: %s: error while adding device '%s': %s", - s->name, d->name, strerror(-r)); + log_debug_errno(r, "idev: %s: error while adding device '%s': %m", + s->name, d->name); return r; } @@ -353,8 +372,8 @@ static int session_remove_device(idev_session *s, idev_device *d) { idev_device_disable(d); if (error < 0) - log_debug("idev: %s: error while removing device '%s': %s", - s->name, d->name, strerror(-error)); + log_debug_errno(error, "idev: %s: error while removing device '%s': %m", + s->name, d->name); idev_device_free(d); return error; } @@ -401,8 +420,8 @@ static int session_remove_element(idev_session *s, idev_element *e) { element_disable(e); if (error < 0) - log_debug("idev: %s: error while removing element '%s': %s", - s->name, e->name, strerror(-r)); + log_debug_errno(r, "idev: %s: error while removing element '%s': %m", + s->name, e->name); idev_element_free(e); return error; } @@ -414,6 +433,98 @@ idev_session *idev_find_session(idev_context *c, const char *name) { return hashmap_get(c->session_map, name); } +static int session_resume_device_fn(sd_bus *bus, + sd_bus_message *signal, + void *userdata, + sd_bus_error *ret_error) { + idev_session *s = userdata; + idev_element *e; + uint32_t major, minor; + int r, fd; + + r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd); + if (r < 0) { + log_debug("idev: %s: erroneous ResumeDevice signal", s->name); + return 0; + } + + e = idev_find_evdev(s, makedev(major, minor)); + if (!e) + return 0; + + element_resume(e, fd); + return 0; +} + +static int session_pause_device_fn(sd_bus *bus, + sd_bus_message *signal, + void *userdata, + sd_bus_error *ret_error) { + idev_session *s = userdata; + idev_element *e; + uint32_t major, minor; + const char *mode; + int r; + + r = sd_bus_message_read(signal, "uus", &major, &minor, &mode); + if (r < 0) { + log_debug("idev: %s: erroneous PauseDevice signal", s->name); + return 0; + } + + e = idev_find_evdev(s, makedev(major, minor)); + if (!e) + return 0; + + element_pause(e, mode); + return 0; +} + +static int session_setup_bus(idev_session *s) { + _cleanup_free_ char *match = NULL; + int r; + + if (!s->managed) + return 0; + + match = strjoin("type='signal'," + "sender='org.freedesktop.login1'," + "interface='org.freedesktop.login1.Session'," + "member='ResumeDevice'," + "path='", s->path, "'", + NULL); + if (!match) + return -ENOMEM; + + r = sd_bus_add_match(s->context->sysbus, + &s->slot_resume_device, + match, + session_resume_device_fn, + s); + if (r < 0) + return r; + + free(match); + match = strjoin("type='signal'," + "sender='org.freedesktop.login1'," + "interface='org.freedesktop.login1.Session'," + "member='PauseDevice'," + "path='", s->path, "'", + NULL); + if (!match) + return -ENOMEM; + + r = sd_bus_add_match(s->context->sysbus, + &s->slot_pause_device, + match, + session_pause_device_fn, + s); + if (r < 0) + return r; + + return 0; +} + int idev_session_new(idev_session **out, idev_context *c, unsigned int flags, @@ -451,14 +562,18 @@ int idev_session_new(idev_session **out, return r; } - s->element_map = hashmap_new(string_hash_func, string_compare_func); + s->element_map = hashmap_new(&string_hash_ops); if (!s->element_map) return -ENOMEM; - s->device_map = hashmap_new(string_hash_func, string_compare_func); + s->device_map = hashmap_new(&string_hash_ops); if (!s->device_map) return -ENOMEM; + r = session_setup_bus(s); + if (r < 0) + return r; + r = hashmap_put(c->session_map, s->name, s); if (r < 0) return r; @@ -482,6 +597,8 @@ idev_session *idev_session_free(idev_session *s) { if (s->name) hashmap_remove_value(s->context->session_map, s->name, s); + s->slot_pause_device = sd_bus_slot_unref(s->slot_pause_device); + s->slot_resume_device = sd_bus_slot_unref(s->slot_resume_device); s->context = idev_context_unref(s->context); hashmap_free(s->device_map); hashmap_free(s->element_map); @@ -522,6 +639,108 @@ void idev_session_disable(idev_session *s) { } } +static int add_link(idev_element *e, idev_device *d) { + idev_link *l; + + assert(e); + assert(d); + + l = new0(idev_link, 1); + if (!l) + return -ENOMEM; + + l->element = e; + l->device = d; + LIST_PREPEND(links_by_element, e->links, l); + LIST_PREPEND(links_by_device, d->links, l); + device_attach(d, l); + + return 0; +} + +static int guess_type(struct udev_device *d) { + const char *id_key; + + id_key = udev_device_get_property_value(d, "ID_INPUT_KEY"); + if (streq_ptr(id_key, "1")) + return IDEV_DEVICE_KEYBOARD; + + return IDEV_DEVICE_CNT; +} + +int idev_session_add_evdev(idev_session *s, struct udev_device *ud) { + idev_element *e; + idev_device *d; + dev_t devnum; + int r, type; + + assert_return(s, -EINVAL); + assert_return(ud, -EINVAL); + + devnum = udev_device_get_devnum(ud); + if (devnum == 0) + return 0; + + e = idev_find_evdev(s, devnum); + if (e) + return 0; + + r = idev_evdev_new(&e, s, ud); + if (r < 0) + return r; + + r = session_add_element(s, e); + if (r != 0) + return r; + + type = guess_type(ud); + if (type < 0) + return type; + + switch (type) { + case IDEV_DEVICE_KEYBOARD: + d = idev_find_keyboard(s, e->name); + if (d) { + log_debug("idev: %s: keyboard for new evdev element '%s' already available", + s->name, e->name); + return 0; + } + + r = idev_keyboard_new(&d, s, e->name); + if (r < 0) + return r; + + r = add_link(e, d); + if (r < 0) { + idev_device_free(d); + return r; + } + + return session_add_device(s, d); + default: + /* unknown elements are silently ignored */ + return 0; + } +} + +int idev_session_remove_evdev(idev_session *s, struct udev_device *ud) { + idev_element *e; + dev_t devnum; + + assert(s); + assert(ud); + + devnum = udev_device_get_devnum(ud); + if (devnum == 0) + return 0; + + e = idev_find_evdev(s, devnum); + if (!e) + return 0; + + return session_remove_element(s, e); +} + /* * Contexts */ @@ -542,11 +761,11 @@ int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus) { if (sysbus) c->sysbus = sd_bus_ref(sysbus); - c->session_map = hashmap_new(string_hash_func, string_compare_func); + c->session_map = hashmap_new(&string_hash_ops); if (!c->session_map) return -ENOMEM; - c->data_map = hashmap_new(string_hash_func, string_compare_func); + c->data_map = hashmap_new(&string_hash_ops); if (!c->data_map) return -ENOMEM;