1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <systemd/sd-bus.h>
26 #include <systemd/sd-event.h>
27 #include <systemd/sd-login.h>
30 #include "idev-internal.h"
31 #include "login-shared.h"
36 static void element_open(idev_element *e);
37 static void element_close(idev_element *e);
43 idev_device *idev_find_device(idev_session *s, const char *name) {
44 assert_return(s, NULL);
45 assert_return(name, NULL);
47 return hashmap_get(s->device_map, name);
50 int idev_device_add(idev_device *d, const char *name) {
53 assert_return(d, -EINVAL);
54 assert_return(d->vtable, -EINVAL);
55 assert_return(d->session, -EINVAL);
56 assert_return(name, -EINVAL);
58 d->name = strdup(name);
62 r = hashmap_put(d->session->device_map, d->name, d);
69 idev_device *idev_device_free(idev_device *d) {
79 assert(d->vtable->free);
82 hashmap_remove_value(d->session->device_map, d->name, d);
92 int idev_device_feed(idev_device *d, idev_data *data) {
95 assert(data->type < IDEV_DATA_CNT);
98 return d->vtable->feed(d, data);
103 void idev_device_feedback(idev_device *d, idev_data *data) {
108 assert(data->type < IDEV_DATA_CNT);
110 LIST_FOREACH(links_by_device, l, d->links)
111 idev_element_feedback(l->element, data);
114 static void device_attach(idev_device *d, idev_link *l) {
118 if (d->vtable->attach)
119 d->vtable->attach(d, l);
122 element_open(l->element);
125 static void device_detach(idev_device *d, idev_link *l) {
130 element_close(l->element);
132 if (d->vtable->detach)
133 d->vtable->detach(d, l);
136 void idev_device_enable(idev_device *d) {
143 LIST_FOREACH(links_by_device, l, d->links)
144 element_open(l->element);
148 void idev_device_disable(idev_device *d) {
155 LIST_FOREACH(links_by_device, l, d->links)
156 element_close(l->element);
164 idev_element *idev_find_element(idev_session *s, const char *name) {
165 assert_return(s, NULL);
166 assert_return(name, NULL);
168 return hashmap_get(s->element_map, name);
171 int idev_element_add(idev_element *e, const char *name) {
174 assert_return(e, -EINVAL);
175 assert_return(e->vtable, -EINVAL);
176 assert_return(e->session, -EINVAL);
177 assert_return(name, -EINVAL);
179 e->name = strdup(name);
183 r = hashmap_put(e->session->element_map, e->name, e);
190 idev_element *idev_element_free(idev_element *e) {
198 assert(e->n_open == 0);
200 assert(e->vtable->free);
203 hashmap_remove_value(e->session->element_map, e->name, e);
213 int idev_element_feed(idev_element *e, idev_data *data) {
219 assert(data->type < IDEV_DATA_CNT);
221 LIST_FOREACH(links_by_element, l, e->links) {
222 r = idev_device_feed(l->device, data);
230 void idev_element_feedback(idev_element *e, idev_data *data) {
233 assert(data->type < IDEV_DATA_CNT);
235 if (e->vtable->feedback)
236 e->vtable->feedback(e, data);
239 static void element_open(idev_element *e) {
242 if (e->n_open++ == 0 && e->vtable->open)
246 static void element_close(idev_element *e) {
248 assert(e->n_open > 0);
250 if (--e->n_open == 0 && e->vtable->close)
254 static void element_enable(idev_element *e) {
259 if (e->vtable->enable)
260 e->vtable->enable(e);
264 static void element_disable(idev_element *e) {
269 if (e->vtable->disable)
270 e->vtable->disable(e);
278 static int session_raise(idev_session *s, idev_event *ev) {
279 return s->event_fn(s, s->userdata, ev);
282 static int session_raise_device_add(idev_session *s, idev_device *d) {
284 .type = IDEV_EVENT_DEVICE_ADD,
290 return session_raise(s, &event);
293 static int session_raise_device_remove(idev_session *s, idev_device *d) {
295 .type = IDEV_EVENT_DEVICE_REMOVE,
301 return session_raise(s, &event);
304 int idev_session_raise_device_data(idev_session *s, idev_device *d, idev_data *data) {
306 .type = IDEV_EVENT_DEVICE_DATA,
313 return session_raise(s, &event);
316 static int session_add_device(idev_session *s, idev_device *d) {
322 log_debug("idev: %s: add device '%s'", s->name, d->name);
325 r = session_raise_device_add(s, d);
335 log_debug("idev: %s: error while adding device '%s': %s",
336 s->name, d->name, strerror(-r));
340 static int session_remove_device(idev_session *s, idev_device *d) {
346 log_debug("idev: %s: remove device '%s'", s->name, d->name);
349 r = session_raise_device_remove(s, d);
353 idev_device_disable(d);
356 log_debug("idev: %s: error while removing device '%s': %s",
357 s->name, d->name, strerror(-error));
362 static int session_add_element(idev_session *s, idev_element *e) {
366 log_debug("idev: %s: add element '%s'", s->name, e->name);
374 static int session_remove_element(idev_session *s, idev_element *e) {
382 log_debug("idev: %s: remove element '%s'", s->name, e->name);
384 while ((l = e->links)) {
386 LIST_REMOVE(links_by_device, d->links, l);
387 LIST_REMOVE(links_by_element, e->links, l);
391 r = session_remove_device(s, d);
404 log_debug("idev: %s: error while removing element '%s': %s",
405 s->name, e->name, strerror(-r));
406 idev_element_free(e);
410 idev_session *idev_find_session(idev_context *c, const char *name) {
411 assert_return(c, NULL);
412 assert_return(name, NULL);
414 return hashmap_get(c->session_map, name);
417 int idev_session_new(idev_session **out,
421 idev_event_fn event_fn,
423 _cleanup_(idev_session_freep) idev_session *s = NULL;
426 assert_return(out, -EINVAL);
427 assert_return(c, -EINVAL);
428 assert_return(name, -EINVAL);
429 assert_return(event_fn, -EINVAL);
430 assert_return((flags & IDEV_SESSION_CUSTOM) == !session_id_valid(name), -EINVAL);
431 assert_return(!(flags & IDEV_SESSION_CUSTOM) || !(flags & IDEV_SESSION_MANAGED), -EINVAL);
432 assert_return(!(flags & IDEV_SESSION_MANAGED) || c->sysbus, -EINVAL);
434 s = new0(idev_session, 1);
438 s->context = idev_context_ref(c);
439 s->custom = flags & IDEV_SESSION_CUSTOM;
440 s->managed = flags & IDEV_SESSION_MANAGED;
441 s->event_fn = event_fn;
442 s->userdata = userdata;
444 s->name = strdup(name);
449 r = sd_bus_path_encode("/org/freedesktop/login1/session", s->name, &s->path);
454 s->element_map = hashmap_new(string_hash_func, string_compare_func);
458 s->device_map = hashmap_new(string_hash_func, string_compare_func);
462 r = hashmap_put(c->session_map, s->name, s);
471 idev_session *idev_session_free(idev_session *s) {
477 while ((e = hashmap_first(s->element_map)))
478 session_remove_element(s, e);
480 assert(hashmap_size(s->device_map) == 0);
483 hashmap_remove_value(s->context->session_map, s->name, s);
485 s->context = idev_context_unref(s->context);
486 hashmap_free(s->device_map);
487 hashmap_free(s->element_map);
495 bool idev_session_is_enabled(idev_session *s) {
496 return s && s->enabled;
499 void idev_session_enable(idev_session *s) {
507 HASHMAP_FOREACH(e, s->element_map, i)
512 void idev_session_disable(idev_session *s) {
520 HASHMAP_FOREACH(e, s->element_map, i)
529 int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus) {
530 _cleanup_(idev_context_unrefp) idev_context *c = NULL;
532 assert_return(out, -EINVAL);
533 assert_return(event, -EINVAL);
535 c = new0(idev_context, 1);
540 c->event = sd_event_ref(event);
543 c->sysbus = sd_bus_ref(sysbus);
545 c->session_map = hashmap_new(string_hash_func, string_compare_func);
549 c->data_map = hashmap_new(string_hash_func, string_compare_func);
558 static void context_cleanup(idev_context *c) {
559 assert(hashmap_size(c->data_map) == 0);
560 assert(hashmap_size(c->session_map) == 0);
562 hashmap_free(c->data_map);
563 hashmap_free(c->session_map);
564 c->sysbus = sd_bus_unref(c->sysbus);
565 c->event = sd_event_unref(c->event);
569 idev_context *idev_context_ref(idev_context *c) {
570 assert_return(c, NULL);
571 assert_return(c->ref > 0, NULL);
577 idev_context *idev_context_unref(idev_context *c) {
581 assert_return(c->ref > 0, NULL);