]>
Commit | Line | Data |
---|---|---|
e202fa31 DH |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com> | |
7 | ||
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. | |
12 | ||
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. | |
17 | ||
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/>. | |
20 | ***/ | |
21 | ||
c93e5a62 | 22 | #include <libudev.h> |
e202fa31 DH |
23 | #include <stdbool.h> |
24 | #include <stdlib.h> | |
ce1daea1 TA |
25 | #include "sd-bus.h" |
26 | #include "sd-event.h" | |
e202fa31 | 27 | #include "hashmap.h" |
a095315b | 28 | #include "login-util.h" |
e202fa31 | 29 | #include "macro.h" |
e202fa31 | 30 | #include "util.h" |
ce1daea1 TA |
31 | #include "idev.h" |
32 | #include "idev-internal.h" | |
e202fa31 DH |
33 | |
34 | static void element_open(idev_element *e); | |
35 | static void element_close(idev_element *e); | |
36 | ||
37 | /* | |
38 | * Devices | |
39 | */ | |
40 | ||
41 | idev_device *idev_find_device(idev_session *s, const char *name) { | |
42 | assert_return(s, NULL); | |
43 | assert_return(name, NULL); | |
44 | ||
45 | return hashmap_get(s->device_map, name); | |
46 | } | |
47 | ||
48 | int idev_device_add(idev_device *d, const char *name) { | |
49 | int r; | |
50 | ||
51 | assert_return(d, -EINVAL); | |
52 | assert_return(d->vtable, -EINVAL); | |
53 | assert_return(d->session, -EINVAL); | |
54 | assert_return(name, -EINVAL); | |
55 | ||
56 | d->name = strdup(name); | |
57 | if (!d->name) | |
58 | return -ENOMEM; | |
59 | ||
60 | r = hashmap_put(d->session->device_map, d->name, d); | |
61 | if (r < 0) | |
62 | return r; | |
63 | ||
64 | return 0; | |
65 | } | |
66 | ||
67 | idev_device *idev_device_free(idev_device *d) { | |
68 | idev_device tmp; | |
69 | ||
70 | if (!d) | |
71 | return NULL; | |
72 | ||
73 | assert(!d->enabled); | |
74 | assert(!d->public); | |
75 | assert(!d->links); | |
76 | assert(d->vtable); | |
77 | assert(d->vtable->free); | |
78 | ||
79 | if (d->name) | |
80 | hashmap_remove_value(d->session->device_map, d->name, d); | |
81 | ||
82 | tmp = *d; | |
83 | d->vtable->free(d); | |
84 | ||
85 | free(tmp.name); | |
86 | ||
87 | return NULL; | |
88 | } | |
89 | ||
90 | int idev_device_feed(idev_device *d, idev_data *data) { | |
91 | assert(d); | |
92 | assert(data); | |
93 | assert(data->type < IDEV_DATA_CNT); | |
94 | ||
95 | if (d->vtable->feed) | |
96 | return d->vtable->feed(d, data); | |
97 | else | |
98 | return 0; | |
99 | } | |
100 | ||
101 | void idev_device_feedback(idev_device *d, idev_data *data) { | |
102 | idev_link *l; | |
103 | ||
104 | assert(d); | |
105 | assert(data); | |
106 | assert(data->type < IDEV_DATA_CNT); | |
107 | ||
108 | LIST_FOREACH(links_by_device, l, d->links) | |
109 | idev_element_feedback(l->element, data); | |
110 | } | |
111 | ||
112 | static void device_attach(idev_device *d, idev_link *l) { | |
113 | assert(d); | |
114 | assert(l); | |
115 | ||
116 | if (d->vtable->attach) | |
117 | d->vtable->attach(d, l); | |
118 | ||
119 | if (d->enabled) | |
120 | element_open(l->element); | |
121 | } | |
122 | ||
123 | static void device_detach(idev_device *d, idev_link *l) { | |
124 | assert(d); | |
125 | assert(l); | |
126 | ||
127 | if (d->enabled) | |
128 | element_close(l->element); | |
129 | ||
130 | if (d->vtable->detach) | |
131 | d->vtable->detach(d, l); | |
132 | } | |
133 | ||
134 | void idev_device_enable(idev_device *d) { | |
135 | idev_link *l; | |
136 | ||
137 | assert(d); | |
138 | ||
139 | if (!d->enabled) { | |
140 | d->enabled = true; | |
141 | LIST_FOREACH(links_by_device, l, d->links) | |
142 | element_open(l->element); | |
143 | } | |
144 | } | |
145 | ||
146 | void idev_device_disable(idev_device *d) { | |
147 | idev_link *l; | |
148 | ||
149 | assert(d); | |
150 | ||
151 | if (d->enabled) { | |
152 | d->enabled = false; | |
153 | LIST_FOREACH(links_by_device, l, d->links) | |
154 | element_close(l->element); | |
155 | } | |
156 | } | |
157 | ||
158 | /* | |
159 | * Elements | |
160 | */ | |
161 | ||
162 | idev_element *idev_find_element(idev_session *s, const char *name) { | |
163 | assert_return(s, NULL); | |
164 | assert_return(name, NULL); | |
165 | ||
166 | return hashmap_get(s->element_map, name); | |
167 | } | |
168 | ||
169 | int idev_element_add(idev_element *e, const char *name) { | |
170 | int r; | |
171 | ||
172 | assert_return(e, -EINVAL); | |
173 | assert_return(e->vtable, -EINVAL); | |
174 | assert_return(e->session, -EINVAL); | |
175 | assert_return(name, -EINVAL); | |
176 | ||
177 | e->name = strdup(name); | |
178 | if (!e->name) | |
179 | return -ENOMEM; | |
180 | ||
181 | r = hashmap_put(e->session->element_map, e->name, e); | |
182 | if (r < 0) | |
183 | return r; | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | idev_element *idev_element_free(idev_element *e) { | |
189 | idev_element tmp; | |
190 | ||
191 | if (!e) | |
192 | return NULL; | |
193 | ||
194 | assert(!e->enabled); | |
195 | assert(!e->links); | |
196 | assert(e->n_open == 0); | |
197 | assert(e->vtable); | |
198 | assert(e->vtable->free); | |
199 | ||
200 | if (e->name) | |
201 | hashmap_remove_value(e->session->element_map, e->name, e); | |
202 | ||
203 | tmp = *e; | |
204 | e->vtable->free(e); | |
205 | ||
206 | free(tmp.name); | |
207 | ||
208 | return NULL; | |
209 | } | |
210 | ||
211 | int idev_element_feed(idev_element *e, idev_data *data) { | |
212 | int r, error = 0; | |
213 | idev_link *l; | |
214 | ||
215 | assert(e); | |
216 | assert(data); | |
217 | assert(data->type < IDEV_DATA_CNT); | |
218 | ||
219 | LIST_FOREACH(links_by_element, l, e->links) { | |
220 | r = idev_device_feed(l->device, data); | |
221 | if (r != 0) | |
222 | error = r; | |
223 | } | |
224 | ||
225 | return error; | |
226 | } | |
227 | ||
228 | void idev_element_feedback(idev_element *e, idev_data *data) { | |
229 | assert(e); | |
230 | assert(data); | |
231 | assert(data->type < IDEV_DATA_CNT); | |
232 | ||
233 | if (e->vtable->feedback) | |
234 | e->vtable->feedback(e, data); | |
235 | } | |
236 | ||
237 | static void element_open(idev_element *e) { | |
238 | assert(e); | |
239 | ||
240 | if (e->n_open++ == 0 && e->vtable->open) | |
241 | e->vtable->open(e); | |
242 | } | |
243 | ||
244 | static void element_close(idev_element *e) { | |
245 | assert(e); | |
246 | assert(e->n_open > 0); | |
247 | ||
248 | if (--e->n_open == 0 && e->vtable->close) | |
249 | e->vtable->close(e); | |
250 | } | |
251 | ||
252 | static void element_enable(idev_element *e) { | |
253 | assert(e); | |
254 | ||
255 | if (!e->enabled) { | |
256 | e->enabled = true; | |
257 | if (e->vtable->enable) | |
258 | e->vtable->enable(e); | |
259 | } | |
260 | } | |
261 | ||
262 | static void element_disable(idev_element *e) { | |
263 | assert(e); | |
264 | ||
265 | if (e->enabled) { | |
266 | e->enabled = false; | |
267 | if (e->vtable->disable) | |
268 | e->vtable->disable(e); | |
269 | } | |
270 | } | |
271 | ||
5d301b8a DH |
272 | static void element_resume(idev_element *e, int fd) { |
273 | assert(e); | |
274 | assert(fd >= 0); | |
275 | ||
276 | if (e->vtable->resume) | |
277 | e->vtable->resume(e, fd); | |
278 | } | |
279 | ||
280 | static void element_pause(idev_element *e, const char *mode) { | |
281 | assert(e); | |
282 | assert(mode); | |
283 | ||
284 | if (e->vtable->pause) | |
285 | e->vtable->pause(e, mode); | |
286 | } | |
287 | ||
e202fa31 DH |
288 | /* |
289 | * Sessions | |
290 | */ | |
291 | ||
292 | static int session_raise(idev_session *s, idev_event *ev) { | |
293 | return s->event_fn(s, s->userdata, ev); | |
294 | } | |
295 | ||
296 | static int session_raise_device_add(idev_session *s, idev_device *d) { | |
297 | idev_event event = { | |
298 | .type = IDEV_EVENT_DEVICE_ADD, | |
299 | .device_add = { | |
300 | .device = d, | |
301 | }, | |
302 | }; | |
303 | ||
304 | return session_raise(s, &event); | |
305 | } | |
306 | ||
307 | static int session_raise_device_remove(idev_session *s, idev_device *d) { | |
308 | idev_event event = { | |
309 | .type = IDEV_EVENT_DEVICE_REMOVE, | |
310 | .device_remove = { | |
311 | .device = d, | |
312 | }, | |
313 | }; | |
314 | ||
315 | return session_raise(s, &event); | |
316 | } | |
317 | ||
318 | int idev_session_raise_device_data(idev_session *s, idev_device *d, idev_data *data) { | |
319 | idev_event event = { | |
320 | .type = IDEV_EVENT_DEVICE_DATA, | |
321 | .device_data = { | |
322 | .device = d, | |
323 | .data = *data, | |
324 | }, | |
325 | }; | |
326 | ||
327 | return session_raise(s, &event); | |
328 | } | |
329 | ||
330 | static int session_add_device(idev_session *s, idev_device *d) { | |
331 | int r; | |
332 | ||
333 | assert(s); | |
334 | assert(d); | |
335 | ||
336 | log_debug("idev: %s: add device '%s'", s->name, d->name); | |
337 | ||
338 | d->public = true; | |
339 | r = session_raise_device_add(s, d); | |
340 | if (r != 0) { | |
341 | d->public = false; | |
342 | goto error; | |
343 | } | |
344 | ||
345 | return 0; | |
346 | ||
347 | error: | |
348 | if (r < 0) | |
c33b3297 MS |
349 | log_debug_errno(r, "idev: %s: error while adding device '%s': %m", |
350 | s->name, d->name); | |
e202fa31 DH |
351 | return r; |
352 | } | |
353 | ||
354 | static int session_remove_device(idev_session *s, idev_device *d) { | |
355 | int r, error = 0; | |
356 | ||
357 | assert(s); | |
358 | assert(d); | |
359 | ||
360 | log_debug("idev: %s: remove device '%s'", s->name, d->name); | |
361 | ||
362 | d->public = false; | |
363 | r = session_raise_device_remove(s, d); | |
364 | if (r != 0) | |
365 | error = r; | |
366 | ||
367 | idev_device_disable(d); | |
368 | ||
369 | if (error < 0) | |
c33b3297 MS |
370 | log_debug_errno(error, "idev: %s: error while removing device '%s': %m", |
371 | s->name, d->name); | |
e202fa31 DH |
372 | idev_device_free(d); |
373 | return error; | |
374 | } | |
375 | ||
376 | static int session_add_element(idev_session *s, idev_element *e) { | |
377 | assert(s); | |
378 | assert(e); | |
379 | ||
380 | log_debug("idev: %s: add element '%s'", s->name, e->name); | |
381 | ||
382 | if (s->enabled) | |
383 | element_enable(e); | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
388 | static int session_remove_element(idev_session *s, idev_element *e) { | |
389 | int r, error = 0; | |
390 | idev_device *d; | |
391 | idev_link *l; | |
392 | ||
393 | assert(s); | |
394 | assert(e); | |
395 | ||
396 | log_debug("idev: %s: remove element '%s'", s->name, e->name); | |
397 | ||
398 | while ((l = e->links)) { | |
399 | d = l->device; | |
400 | LIST_REMOVE(links_by_device, d->links, l); | |
401 | LIST_REMOVE(links_by_element, e->links, l); | |
402 | device_detach(d, l); | |
403 | ||
404 | if (!d->links) { | |
405 | r = session_remove_device(s, d); | |
406 | if (r != 0) | |
407 | error = r; | |
408 | } | |
409 | ||
410 | l->device = NULL; | |
411 | l->element = NULL; | |
412 | free(l); | |
413 | } | |
414 | ||
415 | element_disable(e); | |
416 | ||
417 | if (error < 0) | |
c33b3297 MS |
418 | log_debug_errno(r, "idev: %s: error while removing element '%s': %m", |
419 | s->name, e->name); | |
e202fa31 DH |
420 | idev_element_free(e); |
421 | return error; | |
422 | } | |
423 | ||
424 | idev_session *idev_find_session(idev_context *c, const char *name) { | |
425 | assert_return(c, NULL); | |
426 | assert_return(name, NULL); | |
427 | ||
428 | return hashmap_get(c->session_map, name); | |
429 | } | |
430 | ||
470d7e17 | 431 | static int session_resume_device_fn(sd_bus_message *signal, |
5d301b8a DH |
432 | void *userdata, |
433 | sd_bus_error *ret_error) { | |
434 | idev_session *s = userdata; | |
435 | idev_element *e; | |
436 | uint32_t major, minor; | |
437 | int r, fd; | |
438 | ||
439 | r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd); | |
440 | if (r < 0) { | |
441 | log_debug("idev: %s: erroneous ResumeDevice signal", s->name); | |
442 | return 0; | |
443 | } | |
444 | ||
445 | e = idev_find_evdev(s, makedev(major, minor)); | |
446 | if (!e) | |
447 | return 0; | |
448 | ||
449 | element_resume(e, fd); | |
450 | return 0; | |
451 | } | |
452 | ||
470d7e17 | 453 | static int session_pause_device_fn(sd_bus_message *signal, |
5d301b8a DH |
454 | void *userdata, |
455 | sd_bus_error *ret_error) { | |
456 | idev_session *s = userdata; | |
457 | idev_element *e; | |
458 | uint32_t major, minor; | |
459 | const char *mode; | |
460 | int r; | |
461 | ||
462 | r = sd_bus_message_read(signal, "uus", &major, &minor, &mode); | |
463 | if (r < 0) { | |
464 | log_debug("idev: %s: erroneous PauseDevice signal", s->name); | |
465 | return 0; | |
466 | } | |
467 | ||
468 | e = idev_find_evdev(s, makedev(major, minor)); | |
469 | if (!e) | |
470 | return 0; | |
471 | ||
472 | element_pause(e, mode); | |
473 | return 0; | |
474 | } | |
475 | ||
476 | static int session_setup_bus(idev_session *s) { | |
477 | _cleanup_free_ char *match = NULL; | |
478 | int r; | |
479 | ||
480 | if (!s->managed) | |
481 | return 0; | |
482 | ||
483 | match = strjoin("type='signal'," | |
484 | "sender='org.freedesktop.login1'," | |
485 | "interface='org.freedesktop.login1.Session'," | |
486 | "member='ResumeDevice'," | |
487 | "path='", s->path, "'", | |
488 | NULL); | |
489 | if (!match) | |
490 | return -ENOMEM; | |
491 | ||
492 | r = sd_bus_add_match(s->context->sysbus, | |
493 | &s->slot_resume_device, | |
494 | match, | |
495 | session_resume_device_fn, | |
496 | s); | |
497 | if (r < 0) | |
498 | return r; | |
499 | ||
500 | free(match); | |
501 | match = strjoin("type='signal'," | |
502 | "sender='org.freedesktop.login1'," | |
503 | "interface='org.freedesktop.login1.Session'," | |
504 | "member='PauseDevice'," | |
505 | "path='", s->path, "'", | |
506 | NULL); | |
507 | if (!match) | |
508 | return -ENOMEM; | |
509 | ||
510 | r = sd_bus_add_match(s->context->sysbus, | |
511 | &s->slot_pause_device, | |
512 | match, | |
513 | session_pause_device_fn, | |
514 | s); | |
515 | if (r < 0) | |
516 | return r; | |
517 | ||
518 | return 0; | |
519 | } | |
520 | ||
e202fa31 DH |
521 | int idev_session_new(idev_session **out, |
522 | idev_context *c, | |
523 | unsigned int flags, | |
524 | const char *name, | |
525 | idev_event_fn event_fn, | |
526 | void *userdata) { | |
527 | _cleanup_(idev_session_freep) idev_session *s = NULL; | |
528 | int r; | |
529 | ||
530 | assert_return(out, -EINVAL); | |
531 | assert_return(c, -EINVAL); | |
532 | assert_return(name, -EINVAL); | |
533 | assert_return(event_fn, -EINVAL); | |
534 | assert_return((flags & IDEV_SESSION_CUSTOM) == !session_id_valid(name), -EINVAL); | |
535 | assert_return(!(flags & IDEV_SESSION_CUSTOM) || !(flags & IDEV_SESSION_MANAGED), -EINVAL); | |
536 | assert_return(!(flags & IDEV_SESSION_MANAGED) || c->sysbus, -EINVAL); | |
537 | ||
538 | s = new0(idev_session, 1); | |
539 | if (!s) | |
540 | return -ENOMEM; | |
541 | ||
542 | s->context = idev_context_ref(c); | |
543 | s->custom = flags & IDEV_SESSION_CUSTOM; | |
544 | s->managed = flags & IDEV_SESSION_MANAGED; | |
545 | s->event_fn = event_fn; | |
546 | s->userdata = userdata; | |
547 | ||
548 | s->name = strdup(name); | |
549 | if (!s->name) | |
550 | return -ENOMEM; | |
551 | ||
552 | if (s->managed) { | |
553 | r = sd_bus_path_encode("/org/freedesktop/login1/session", s->name, &s->path); | |
554 | if (r < 0) | |
555 | return r; | |
556 | } | |
557 | ||
440046e9 | 558 | s->element_map = hashmap_new(&string_hash_ops); |
e202fa31 DH |
559 | if (!s->element_map) |
560 | return -ENOMEM; | |
561 | ||
440046e9 | 562 | s->device_map = hashmap_new(&string_hash_ops); |
e202fa31 DH |
563 | if (!s->device_map) |
564 | return -ENOMEM; | |
565 | ||
5d301b8a DH |
566 | r = session_setup_bus(s); |
567 | if (r < 0) | |
568 | return r; | |
569 | ||
e202fa31 DH |
570 | r = hashmap_put(c->session_map, s->name, s); |
571 | if (r < 0) | |
572 | return r; | |
573 | ||
574 | *out = s; | |
575 | s = NULL; | |
576 | return 0; | |
577 | } | |
578 | ||
579 | idev_session *idev_session_free(idev_session *s) { | |
580 | idev_element *e; | |
581 | ||
582 | if (!s) | |
583 | return NULL; | |
584 | ||
585 | while ((e = hashmap_first(s->element_map))) | |
586 | session_remove_element(s, e); | |
587 | ||
588 | assert(hashmap_size(s->device_map) == 0); | |
589 | ||
590 | if (s->name) | |
591 | hashmap_remove_value(s->context->session_map, s->name, s); | |
592 | ||
5d301b8a DH |
593 | s->slot_pause_device = sd_bus_slot_unref(s->slot_pause_device); |
594 | s->slot_resume_device = sd_bus_slot_unref(s->slot_resume_device); | |
e202fa31 DH |
595 | s->context = idev_context_unref(s->context); |
596 | hashmap_free(s->device_map); | |
597 | hashmap_free(s->element_map); | |
598 | free(s->path); | |
599 | free(s->name); | |
600 | free(s); | |
601 | ||
602 | return NULL; | |
603 | } | |
604 | ||
605 | bool idev_session_is_enabled(idev_session *s) { | |
606 | return s && s->enabled; | |
607 | } | |
608 | ||
609 | void idev_session_enable(idev_session *s) { | |
610 | idev_element *e; | |
611 | Iterator i; | |
612 | ||
613 | assert(s); | |
614 | ||
615 | if (!s->enabled) { | |
616 | s->enabled = true; | |
617 | HASHMAP_FOREACH(e, s->element_map, i) | |
618 | element_enable(e); | |
619 | } | |
620 | } | |
621 | ||
622 | void idev_session_disable(idev_session *s) { | |
623 | idev_element *e; | |
624 | Iterator i; | |
625 | ||
626 | assert(s); | |
627 | ||
628 | if (s->enabled) { | |
629 | s->enabled = false; | |
630 | HASHMAP_FOREACH(e, s->element_map, i) | |
631 | element_disable(e); | |
632 | } | |
633 | } | |
634 | ||
e06cc7b0 DH |
635 | static int add_link(idev_element *e, idev_device *d) { |
636 | idev_link *l; | |
637 | ||
638 | assert(e); | |
639 | assert(d); | |
640 | ||
641 | l = new0(idev_link, 1); | |
642 | if (!l) | |
643 | return -ENOMEM; | |
644 | ||
645 | l->element = e; | |
646 | l->device = d; | |
647 | LIST_PREPEND(links_by_element, e->links, l); | |
648 | LIST_PREPEND(links_by_device, d->links, l); | |
649 | device_attach(d, l); | |
650 | ||
651 | return 0; | |
652 | } | |
653 | ||
654 | static int guess_type(struct udev_device *d) { | |
655 | const char *id_key; | |
656 | ||
657 | id_key = udev_device_get_property_value(d, "ID_INPUT_KEY"); | |
658 | if (streq_ptr(id_key, "1")) | |
659 | return IDEV_DEVICE_KEYBOARD; | |
660 | ||
661 | return IDEV_DEVICE_CNT; | |
662 | } | |
663 | ||
c93e5a62 DH |
664 | int idev_session_add_evdev(idev_session *s, struct udev_device *ud) { |
665 | idev_element *e; | |
e06cc7b0 | 666 | idev_device *d; |
c93e5a62 | 667 | dev_t devnum; |
e06cc7b0 | 668 | int r, type; |
c93e5a62 DH |
669 | |
670 | assert_return(s, -EINVAL); | |
671 | assert_return(ud, -EINVAL); | |
672 | ||
673 | devnum = udev_device_get_devnum(ud); | |
674 | if (devnum == 0) | |
675 | return 0; | |
676 | ||
677 | e = idev_find_evdev(s, devnum); | |
678 | if (e) | |
679 | return 0; | |
680 | ||
681 | r = idev_evdev_new(&e, s, ud); | |
682 | if (r < 0) | |
683 | return r; | |
684 | ||
685 | r = session_add_element(s, e); | |
686 | if (r != 0) | |
687 | return r; | |
688 | ||
e06cc7b0 DH |
689 | type = guess_type(ud); |
690 | if (type < 0) | |
691 | return type; | |
692 | ||
693 | switch (type) { | |
694 | case IDEV_DEVICE_KEYBOARD: | |
695 | d = idev_find_keyboard(s, e->name); | |
696 | if (d) { | |
697 | log_debug("idev: %s: keyboard for new evdev element '%s' already available", | |
698 | s->name, e->name); | |
699 | return 0; | |
700 | } | |
701 | ||
702 | r = idev_keyboard_new(&d, s, e->name); | |
703 | if (r < 0) | |
704 | return r; | |
705 | ||
706 | r = add_link(e, d); | |
707 | if (r < 0) { | |
708 | idev_device_free(d); | |
709 | return r; | |
710 | } | |
711 | ||
712 | return session_add_device(s, d); | |
713 | default: | |
714 | /* unknown elements are silently ignored */ | |
715 | return 0; | |
716 | } | |
c93e5a62 DH |
717 | } |
718 | ||
719 | int idev_session_remove_evdev(idev_session *s, struct udev_device *ud) { | |
720 | idev_element *e; | |
721 | dev_t devnum; | |
722 | ||
723 | assert(s); | |
724 | assert(ud); | |
725 | ||
726 | devnum = udev_device_get_devnum(ud); | |
727 | if (devnum == 0) | |
728 | return 0; | |
729 | ||
730 | e = idev_find_evdev(s, devnum); | |
731 | if (!e) | |
732 | return 0; | |
733 | ||
734 | return session_remove_element(s, e); | |
735 | } | |
736 | ||
e202fa31 DH |
737 | /* |
738 | * Contexts | |
739 | */ | |
740 | ||
741 | int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus) { | |
742 | _cleanup_(idev_context_unrefp) idev_context *c = NULL; | |
743 | ||
744 | assert_return(out, -EINVAL); | |
745 | assert_return(event, -EINVAL); | |
746 | ||
747 | c = new0(idev_context, 1); | |
748 | if (!c) | |
749 | return -ENOMEM; | |
750 | ||
751 | c->ref = 1; | |
752 | c->event = sd_event_ref(event); | |
753 | ||
754 | if (sysbus) | |
755 | c->sysbus = sd_bus_ref(sysbus); | |
756 | ||
440046e9 | 757 | c->session_map = hashmap_new(&string_hash_ops); |
e202fa31 DH |
758 | if (!c->session_map) |
759 | return -ENOMEM; | |
760 | ||
440046e9 | 761 | c->data_map = hashmap_new(&string_hash_ops); |
e202fa31 DH |
762 | if (!c->data_map) |
763 | return -ENOMEM; | |
764 | ||
765 | *out = c; | |
766 | c = NULL; | |
767 | return 0; | |
768 | } | |
769 | ||
770 | static void context_cleanup(idev_context *c) { | |
771 | assert(hashmap_size(c->data_map) == 0); | |
772 | assert(hashmap_size(c->session_map) == 0); | |
773 | ||
774 | hashmap_free(c->data_map); | |
775 | hashmap_free(c->session_map); | |
776 | c->sysbus = sd_bus_unref(c->sysbus); | |
777 | c->event = sd_event_unref(c->event); | |
778 | free(c); | |
779 | } | |
780 | ||
781 | idev_context *idev_context_ref(idev_context *c) { | |
782 | assert_return(c, NULL); | |
783 | assert_return(c->ref > 0, NULL); | |
784 | ||
785 | ++c->ref; | |
786 | return c; | |
787 | } | |
788 | ||
789 | idev_context *idev_context_unref(idev_context *c) { | |
790 | if (!c) | |
791 | return NULL; | |
792 | ||
793 | assert_return(c->ref > 0, NULL); | |
794 | ||
795 | if (--c->ref == 0) | |
796 | context_cleanup(c); | |
797 | ||
798 | return NULL; | |
799 | } |