]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/device.c
dbus: fix minor memory leak when sending job change signals
[thirdparty/systemd.git] / src / core / device.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
5cb5a6ff 2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
a7334b09
LP
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
25ac040b 22#include <errno.h>
f94ea366 23#include <sys/epoll.h>
25ac040b
LP
24#include <libudev.h>
25
87f0e418 26#include "unit.h"
5cb5a6ff
LP
27#include "device.h"
28#include "strv.h"
25ac040b 29#include "log.h"
9e2f7c11 30#include "unit-name.h"
4139c1b2 31#include "dbus-device.h"
f6a6225e 32#include "def.h"
9eb977db 33#include "path-util.h"
5cb5a6ff 34
f50e0a01
LP
35static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
36 [DEVICE_DEAD] = UNIT_INACTIVE,
73608ed9 37 [DEVICE_PLUGGED] = UNIT_ACTIVE
f50e0a01
LP
38};
39
8fe914ec
LP
40static void device_unset_sysfs(Device *d) {
41 Device *first;
42
43 assert(d);
44
a7f241db
LP
45 if (!d->sysfs)
46 return;
47
48 /* Remove this unit from the chain of devices which share the
49 * same sysfs path. */
1124fe6f 50 first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, d->sysfs);
a7f241db 51 LIST_REMOVE(Device, same_sysfs, first, d);
8fe914ec 52
a7f241db 53 if (first)
1124fe6f 54 hashmap_remove_and_replace(UNIT(d)->manager->devices_by_sysfs, d->sysfs, first->sysfs, first);
a7f241db 55 else
1124fe6f 56 hashmap_remove(UNIT(d)->manager->devices_by_sysfs, d->sysfs);
a7f241db
LP
57
58 free(d->sysfs);
59 d->sysfs = NULL;
8fe914ec
LP
60}
61
faf919f1
LP
62static void device_init(Unit *u) {
63 Device *d = DEVICE(u);
64
65 assert(d);
1124fe6f 66 assert(UNIT(d)->load_state == UNIT_STUB);
faf919f1 67
8fe914ec
LP
68 /* In contrast to all other unit types we timeout jobs waiting
69 * for devices by default. This is because they otherwise wait
35b8ca3a 70 * indefinitely for plugged in devices, something which cannot
8fe914ec
LP
71 * happen for the other units since their operations time out
72 * anyway. */
1124fe6f 73 UNIT(d)->job_timeout = DEFAULT_TIMEOUT_USEC;
c8f4d764 74
1124fe6f
MS
75 UNIT(d)->ignore_on_isolate = true;
76 UNIT(d)->ignore_on_snapshot = true;
faf919f1
LP
77}
78
87f0e418
LP
79static void device_done(Unit *u) {
80 Device *d = DEVICE(u);
034c6ed7
LP
81
82 assert(d);
e537352b 83
8fe914ec 84 device_unset_sysfs(d);
e537352b
LP
85}
86
f50e0a01
LP
87static void device_set_state(Device *d, DeviceState state) {
88 DeviceState old_state;
89 assert(d);
5cb5a6ff 90
f50e0a01
LP
91 old_state = d->state;
92 d->state = state;
5cb5a6ff 93
e537352b 94 if (state != old_state)
66870f90
ZJS
95 log_debug_unit(UNIT(d)->id,
96 "%s changed %s -> %s", UNIT(d)->id,
97 device_state_to_string(old_state),
98 device_state_to_string(state));
f50e0a01 99
e2f3b44c 100 unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true);
f50e0a01
LP
101}
102
103static int device_coldplug(Unit *u) {
104 Device *d = DEVICE(u);
105
106 assert(d);
107 assert(d->state == DEVICE_DEAD);
108
109 if (d->sysfs)
73608ed9 110 device_set_state(d, DEVICE_PLUGGED);
f50e0a01
LP
111
112 return 0;
113}
114
115static void device_dump(Unit *u, FILE *f, const char *prefix) {
25ac040b 116 Device *d = DEVICE(u);
5cb5a6ff 117
25ac040b 118 assert(d);
5cb5a6ff
LP
119
120 fprintf(f,
25ac040b
LP
121 "%sDevice State: %s\n"
122 "%sSysfs Path: %s\n",
a16e1123 123 prefix, device_state_to_string(d->state),
f50e0a01
LP
124 prefix, strna(d->sysfs));
125}
126
44a6b1b6 127_pure_ static UnitActiveState device_active_state(Unit *u) {
f50e0a01
LP
128 assert(u);
129
130 return state_translation_table[DEVICE(u)->state];
25ac040b
LP
131}
132
44a6b1b6 133_pure_ static const char *device_sub_state_to_string(Unit *u) {
10a94420
LP
134 assert(u);
135
a16e1123 136 return device_state_to_string(DEVICE(u)->state);
10a94420
LP
137}
138
8fe914ec 139static int device_add_escaped_name(Unit *u, const char *dn) {
25ac040b
LP
140 char *e;
141 int r;
142
143 assert(u);
144 assert(dn);
145 assert(dn[0] == '/');
146
b0193f1c
LP
147 e = unit_name_from_path(dn, ".device");
148 if (!e)
25ac040b
LP
149 return -ENOMEM;
150
151 r = unit_add_name(u, e);
152 free(e);
153
154 if (r < 0 && r != -EEXIST)
155 return r;
156
157 return 0;
158}
159
aab14b13
LP
160static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
161 char *e;
162 Unit *u;
163
164 assert(m);
165 assert(dn);
166 assert(dn[0] == '/');
167 assert(_u);
168
b0193f1c
LP
169 e = unit_name_from_path(dn, ".device");
170 if (!e)
aab14b13
LP
171 return -ENOMEM;
172
173 u = manager_get_unit(m, e);
174 free(e);
175
176 if (u) {
177 *_u = u;
178 return 1;
179 }
180
181 return 0;
182}
183
8fe914ec
LP
184static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
185 const char *sysfs, *model;
25ac040b
LP
186 Unit *u = NULL;
187 int r;
25ac040b 188 bool delete;
25ac040b
LP
189
190 assert(m);
191
7f275a9f
LP
192 if (!(sysfs = udev_device_get_syspath(dev)))
193 return -ENOMEM;
194
8fe914ec 195 if ((r = device_find_escape_name(m, path, &u)) < 0)
aab14b13 196 return r;
25ac040b 197
5845b46b 198 if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
8fe914ec 199 return -EEXIST;
25ac040b 200
aab14b13
LP
201 if (!u) {
202 delete = true;
203
7d17cfbc
MS
204 u = unit_new(m, sizeof(Device));
205 if (!u)
aab14b13 206 return -ENOMEM;
25ac040b 207
7d17cfbc
MS
208 r = device_add_escaped_name(u, path);
209 if (r < 0)
25ac040b
LP
210 goto fail;
211
ee6cb288
LP
212 unit_add_to_load_queue(u);
213 } else
214 delete = false;
215
216 /* If this was created via some dependency and has not
217 * actually been seen yet ->sysfs will not be
218 * initialized. Hence initialize it if necessary. */
219
220 if (!DEVICE(u)->sysfs) {
221 Device *first;
222
25ac040b
LP
223 if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
224 r = -ENOMEM;
225 goto fail;
226 }
227
8fe914ec
LP
228 if (!m->devices_by_sysfs)
229 if (!(m->devices_by_sysfs = hashmap_new(string_hash_func, string_compare_func))) {
230 r = -ENOMEM;
231 goto fail;
232 }
25ac040b 233
8fe914ec
LP
234 first = hashmap_get(m->devices_by_sysfs, sysfs);
235 LIST_PREPEND(Device, same_sysfs, first, DEVICE(u));
236
237 if ((r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first)) < 0)
25ac040b 238 goto fail;
ee6cb288 239 }
8fe914ec 240
aab14b13
LP
241 if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
242 (model = udev_device_get_property_value(dev, "ID_MODEL"))) {
243 if ((r = unit_set_description(u, model)) < 0)
244 goto fail;
4e85aff4 245 } else
8fe914ec 246 if ((r = unit_set_description(u, path)) < 0)
4e85aff4 247 goto fail;
aab14b13 248
8fe914ec
LP
249 if (main) {
250 /* The additional systemd udev properties we only
251 * interpret for the main object */
252 const char *wants, *alias;
253
07845c14
KS
254 alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS");
255 if (alias) {
256 char *state, *w;
257 size_t l;
258
259 FOREACH_WORD_QUOTED(w, l, alias, state) {
260 char *e;
261
262 e = strndup(w, l);
263 if (!e) {
264 r = -ENOMEM;
8fe914ec 265 goto fail;
07845c14
KS
266 }
267
268 if (!is_path(e)) {
269 log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, e);
270 free(e);
271 } else {
22349cee 272 device_update_unit(m, dev, e, false);
07845c14 273 free(e);
07845c14 274 }
b866264a 275 }
8fe914ec 276 }
25ac040b 277
07845c14
KS
278 wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS");
279 if (wants) {
8fe914ec
LP
280 char *state, *w;
281 size_t l;
25ac040b 282
8fe914ec 283 FOREACH_WORD_QUOTED(w, l, wants, state) {
d9abd149 284 char *e, *n;
8fe914ec 285
07845c14
KS
286 e = strndup(w, l);
287 if (!e) {
8fe914ec
LP
288 r = -ENOMEM;
289 goto fail;
290 }
d9abd149
MT
291 n = unit_name_mangle(e);
292 if (!n) {
293 r = -ENOMEM;
294 goto fail;
295 }
8fe914ec 296 free(e);
d9abd149
MT
297
298 r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true);
299 free(n);
8fe914ec
LP
300 if (r < 0)
301 goto fail;
302 }
25ac040b 303 }
a7f241db 304 }
f94ea366 305
c1e1601e 306 unit_add_to_dbus_queue(u);
25ac040b
LP
307 return 0;
308
309fail:
ee5f3479
LP
310 log_warning("Failed to load device unit: %s", strerror(-r));
311
25ac040b
LP
312 if (delete && u)
313 unit_free(u);
ee5f3479 314
25ac040b
LP
315 return r;
316}
317
8fe914ec
LP
318static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
319 const char *sysfs, *dn;
320 struct udev_list_entry *item = NULL, *first = NULL;
003ac9d0 321 int r;
8fe914ec
LP
322
323 assert(m);
324
325 if (!(sysfs = udev_device_get_syspath(dev)))
326 return -ENOMEM;
327
328 /* Add the main unit named after the sysfs path */
003ac9d0
HH
329 r = device_update_unit(m, dev, sysfs, true);
330 if (r < 0)
331 return r;
8fe914ec
LP
332
333 /* Add an additional unit for the device node */
334 if ((dn = udev_device_get_devnode(dev)))
335 device_update_unit(m, dev, dn, false);
336
337 /* Add additional units for all symlinks */
338 first = udev_device_get_devlinks_list_entry(dev);
339 udev_list_entry_foreach(item, first) {
340 const char *p;
5845b46b 341 struct stat st;
8fe914ec
LP
342
343 /* Don't bother with the /dev/block links */
344 p = udev_list_entry_get_name(item);
345
346 if (path_startswith(p, "/dev/block/") ||
347 path_startswith(p, "/dev/char/"))
348 continue;
349
5845b46b
LP
350 /* Verify that the symlink in the FS actually belongs
351 * to this device. This is useful to deal with
352 * conflicting devices, e.g. when two disks want the
353 * same /dev/disk/by-label/xxx link because they have
354 * the same label. We want to make sure that the same
355 * device that won the symlink wins in systemd, so we
356 * check the device node major/minor*/
357 if (stat(p, &st) >= 0)
358 if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
359 st.st_rdev != udev_device_get_devnum(dev))
360 continue;
361
8fe914ec
LP
362 device_update_unit(m, dev, p, false);
363 }
364
365 if (update_state) {
366 Device *d, *l;
367
368 manager_dispatch_load_queue(m);
369
370 l = hashmap_get(m->devices_by_sysfs, sysfs);
371 LIST_FOREACH(same_sysfs, d, l)
372 device_set_state(d, DEVICE_PLUGGED);
373 }
374
375 return 0;
376}
377
f94ea366 378static int device_process_path(Manager *m, const char *path, bool update_state) {
25ac040b
LP
379 int r;
380 struct udev_device *dev;
381
382 assert(m);
383 assert(path);
384
385 if (!(dev = udev_device_new_from_syspath(m->udev, path))) {
386 log_warning("Failed to get udev device object from udev for path %s.", path);
387 return -ENOMEM;
388 }
389
f94ea366 390 r = device_process_new_device(m, dev, update_state);
25ac040b
LP
391 udev_device_unref(dev);
392 return r;
393}
394
f94ea366
LP
395static int device_process_removed_device(Manager *m, struct udev_device *dev) {
396 const char *sysfs;
f94ea366
LP
397 Device *d;
398
399 assert(m);
400 assert(dev);
401
402 if (!(sysfs = udev_device_get_syspath(dev)))
403 return -ENOMEM;
404
8fe914ec
LP
405 /* Remove all units of this sysfs path */
406 while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) {
407 device_unset_sysfs(d);
408 device_set_state(d, DEVICE_DEAD);
409 }
f94ea366 410
f94ea366
LP
411 return 0;
412}
413
a7f241db
LP
414static Unit *device_following(Unit *u) {
415 Device *d = DEVICE(u);
416 Device *other, *first = NULL;
417
418 assert(d);
419
ac155bb8 420 if (startswith(u->id, "sys-"))
a7f241db
LP
421 return NULL;
422
423 /* Make everybody follow the unit that's named after the sysfs path */
424 for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
1124fe6f 425 if (startswith(UNIT(other)->id, "sys-"))
a7f241db
LP
426 return UNIT(other);
427
428 for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) {
1124fe6f 429 if (startswith(UNIT(other)->id, "sys-"))
a7f241db
LP
430 return UNIT(other);
431
432 first = other;
433 }
434
435 return UNIT(first);
436}
437
6210e7fc
LP
438static int device_following_set(Unit *u, Set **_s) {
439 Device *d = DEVICE(u);
440 Device *other;
441 Set *s;
442 int r;
443
444 assert(d);
445 assert(_s);
446
447 if (!d->same_sysfs_prev && !d->same_sysfs_next) {
448 *_s = NULL;
449 return 0;
450 }
451
452 if (!(s = set_new(NULL, NULL)))
453 return -ENOMEM;
454
455 for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
456 if ((r = set_put(s, other)) < 0)
457 goto fail;
458
459 for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev)
460 if ((r = set_put(s, other)) < 0)
461 goto fail;
462
463 *_s = s;
464 return 1;
465
466fail:
467 set_free(s);
468 return r;
469}
470
25ac040b
LP
471static void device_shutdown(Manager *m) {
472 assert(m);
473
a16e1123 474 if (m->udev_monitor) {
f94ea366 475 udev_monitor_unref(m->udev_monitor);
a16e1123
LP
476 m->udev_monitor = NULL;
477 }
f94ea366 478
a16e1123 479 if (m->udev) {
25ac040b 480 udev_unref(m->udev);
a16e1123
LP
481 m->udev = NULL;
482 }
8fe914ec
LP
483
484 hashmap_free(m->devices_by_sysfs);
485 m->devices_by_sysfs = NULL;
25ac040b
LP
486}
487
488static int device_enumerate(Manager *m) {
489 int r;
490 struct udev_enumerate *e = NULL;
491 struct udev_list_entry *item = NULL, *first = NULL;
492
493 assert(m);
494
a16e1123 495 if (!m->udev) {
b92bea5d
ZJS
496 struct epoll_event ev;
497
a16e1123
LP
498 if (!(m->udev = udev_new()))
499 return -ENOMEM;
25ac040b 500
a16e1123
LP
501 if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
502 r = -ENOMEM;
503 goto fail;
504 }
f94ea366 505
47ae6e67
LP
506 /* This will fail if we are unprivileged, but that
507 * should not matter much, as user instances won't run
508 * during boot. */
509 udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024);
99448c1f 510
e1ce2c27
LP
511 if (udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd") < 0) {
512 r = -ENOMEM;
513 goto fail;
514 }
515
a16e1123
LP
516 if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
517 r = -EIO;
518 goto fail;
519 }
f94ea366 520
a16e1123
LP
521 m->udev_watch.type = WATCH_UDEV;
522 m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
f94ea366 523
a16e1123
LP
524 zero(ev);
525 ev.events = EPOLLIN;
526 ev.data.ptr = &m->udev_watch;
f94ea366 527
a16e1123
LP
528 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
529 return -errno;
530 }
f94ea366 531
4f2d528d
LP
532 if (!(e = udev_enumerate_new(m->udev))) {
533 r = -ENOMEM;
534 goto fail;
535 }
e1ce2c27
LP
536 if (udev_enumerate_add_match_tag(e, "systemd") < 0) {
537 r = -EIO;
538 goto fail;
539 }
25ac040b 540
4f2d528d
LP
541 if (udev_enumerate_scan_devices(e) < 0) {
542 r = -EIO;
543 goto fail;
544 }
25ac040b 545
4f2d528d
LP
546 first = udev_enumerate_get_list_entry(e);
547 udev_list_entry_foreach(item, first)
548 device_process_path(m, udev_list_entry_get_name(item), false);
25ac040b 549
4f2d528d 550 udev_enumerate_unref(e);
25ac040b
LP
551 return 0;
552
553fail:
554 if (e)
555 udev_enumerate_unref(e);
556
557 device_shutdown(m);
558 return r;
5cb5a6ff
LP
559}
560
f94ea366
LP
561void device_fd_event(Manager *m, int events) {
562 struct udev_device *dev;
563 int r;
2958c886 564 const char *action, *ready;
f94ea366
LP
565
566 assert(m);
99448c1f
KS
567
568 if (events != EPOLLIN) {
569 static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5);
570
571 if (!ratelimit_test(&limit))
572 log_error("Failed to get udev event: %m");
2e6081f2
KS
573 if (!(events & EPOLLIN))
574 return;
99448c1f 575 }
f94ea366 576
f94ea366 577 if (!(dev = udev_monitor_receive_device(m->udev_monitor))) {
99448c1f
KS
578 /*
579 * libudev might filter-out devices which pass the bloom filter,
35b8ca3a 580 * so getting NULL here is not necessarily an error
99448c1f 581 */
f94ea366
LP
582 return;
583 }
584
585 if (!(action = udev_device_get_action(dev))) {
586 log_error("Failed to get udev action string.");
587 goto fail;
588 }
589
2958c886
LP
590 ready = udev_device_get_property_value(dev, "SYSTEMD_READY");
591
592 if (streq(action, "remove") || (ready && parse_boolean(ready) == 0)) {
f94ea366
LP
593 if ((r = device_process_removed_device(m, dev)) < 0) {
594 log_error("Failed to process udev device event: %s", strerror(-r));
595 goto fail;
596 }
597 } else {
598 if ((r = device_process_new_device(m, dev, true)) < 0) {
599 log_error("Failed to process udev device event: %s", strerror(-r));
600 goto fail;
601 }
602 }
603
604fail:
605 udev_device_unref(dev);
606}
607
a16e1123
LP
608static const char* const device_state_table[_DEVICE_STATE_MAX] = {
609 [DEVICE_DEAD] = "dead",
73608ed9 610 [DEVICE_PLUGGED] = "plugged"
a16e1123
LP
611};
612
613DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
614
87f0e418 615const UnitVTable device_vtable = {
7d17cfbc 616 .object_size = sizeof(Device),
f975e971
LP
617 .sections =
618 "Unit\0"
619 "Device\0"
620 "Install\0",
5cb5a6ff 621
9e2f7c11
LP
622 .no_instances = true,
623
faf919f1
LP
624 .init = device_init,
625
e537352b 626 .load = unit_load_fragment_and_dropin_optional,
034c6ed7 627 .done = device_done,
f50e0a01
LP
628 .coldplug = device_coldplug,
629
5cb5a6ff
LP
630 .dump = device_dump,
631
f50e0a01 632 .active_state = device_active_state,
10a94420 633 .sub_state_to_string = device_sub_state_to_string,
25ac040b 634
c4e2ceae 635 .bus_interface = "org.freedesktop.systemd1.Device",
4139c1b2 636 .bus_message_handler = bus_device_message_handler,
c4e2ceae 637 .bus_invalidating_properties = bus_device_invalidating_properties,
4139c1b2 638
a7f241db 639 .following = device_following,
6210e7fc 640 .following_set = device_following_set,
a7f241db 641
f50e0a01 642 .enumerate = device_enumerate,
c6918296
MS
643 .shutdown = device_shutdown,
644
645 .status_message_formats = {
646 .starting_stopping = {
647 [0] = "Expecting device %s...",
648 },
649 .finished_start_job = {
650 [JOB_DONE] = "Found device %s.",
651 [JOB_TIMEOUT] = "Timed out waiting for device %s.",
652 },
653 },
5cb5a6ff 654};