]> git.ipfire.org Git - people/ms/systemd.git/blame - device.c
mount: only add those mount points to localfs.target as Wants that are marked for us
[people/ms/systemd.git] / device.c
CommitLineData
5cb5a6ff
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
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
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
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"
5cb5a6ff 30
f50e0a01
LP
31static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
32 [DEVICE_DEAD] = UNIT_INACTIVE,
33 [DEVICE_AVAILABLE] = UNIT_ACTIVE
34};
35
36static const char* const state_string_table[_DEVICE_STATE_MAX] = {
37 [DEVICE_DEAD] = "dead",
38 [DEVICE_AVAILABLE] = "available"
39};
40
87f0e418
LP
41static void device_done(Unit *u) {
42 Device *d = DEVICE(u);
034c6ed7
LP
43
44 assert(d);
e537352b 45
25ac040b 46 free(d->sysfs);
e537352b
LP
47 d->sysfs = NULL;
48}
49
50static void device_init(Unit *u) {
51 Device *d = DEVICE(u);
52
53 assert(d);
54 assert(u->meta.load_state == UNIT_STUB);
55
56 d->state = 0;
034c6ed7
LP
57}
58
f50e0a01
LP
59static void device_set_state(Device *d, DeviceState state) {
60 DeviceState old_state;
61 assert(d);
5cb5a6ff 62
f50e0a01
LP
63 old_state = d->state;
64 d->state = state;
5cb5a6ff 65
e537352b
LP
66 if (state != old_state)
67 log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]);
f50e0a01
LP
68
69 unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
70}
71
72static int device_coldplug(Unit *u) {
73 Device *d = DEVICE(u);
74
75 assert(d);
76 assert(d->state == DEVICE_DEAD);
77
78 if (d->sysfs)
79 device_set_state(d, DEVICE_AVAILABLE);
80
81 return 0;
82}
83
84static void device_dump(Unit *u, FILE *f, const char *prefix) {
25ac040b 85 Device *d = DEVICE(u);
5cb5a6ff 86
25ac040b 87 assert(d);
5cb5a6ff
LP
88
89 fprintf(f,
25ac040b
LP
90 "%sDevice State: %s\n"
91 "%sSysfs Path: %s\n",
f50e0a01
LP
92 prefix, state_string_table[d->state],
93 prefix, strna(d->sysfs));
94}
95
96static UnitActiveState device_active_state(Unit *u) {
97 assert(u);
98
99 return state_translation_table[DEVICE(u)->state];
25ac040b
LP
100}
101
10a94420
LP
102static const char *device_sub_state_to_string(Unit *u) {
103 assert(u);
104
105 return state_string_table[DEVICE(u)->state];
106}
107
2e478a46 108static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
25ac040b
LP
109 char *e;
110 int r;
111
112 assert(u);
113 assert(dn);
114 assert(dn[0] == '/');
115
2e478a46 116 if (!(e = unit_name_escape_path(dn+1, ".device")))
25ac040b
LP
117 return -ENOMEM;
118
119 r = unit_add_name(u, e);
b08d03ff
LP
120
121 if (r >= 0 && make_id)
122 unit_choose_id(u, e);
123
25ac040b
LP
124 free(e);
125
126 if (r < 0 && r != -EEXIST)
127 return r;
128
129 return 0;
130}
131
aab14b13
LP
132static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
133 char *e;
134 Unit *u;
135
136 assert(m);
137 assert(dn);
138 assert(dn[0] == '/');
139 assert(_u);
140
141 if (!(e = unit_name_escape_path(dn+1, ".device")))
142 return -ENOMEM;
143
144 u = manager_get_unit(m, e);
145 free(e);
146
147 if (u) {
148 *_u = u;
149 return 1;
150 }
151
152 return 0;
153}
154
6326a423
LP
155static bool devnode_is_api(const char *node) {
156 unsigned i;
157
158 static const char * const table[] = {
159 "/dev/null",
160 "/dev/zero",
161 "/dev/urandom",
162 "/dev/random",
163 "/dev/port",
164 "/dev/oldmem",
165 "/dev/full",
166 "/dev/kmsg",
167 "/dev/mem"
168 };
169
170 for (i = 0; i < ELEMENTSOF(table); i++)
171 if (streq(table[i], node))
172 return true;
173
174 return false;
175}
176
f94ea366 177static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
aab14b13 178 const char *dn, *names, *wants, *sysfs, *expose, *model;
25ac040b
LP
179 Unit *u = NULL;
180 int r;
aab14b13 181 char *w, *state;
25ac040b
LP
182 size_t l;
183 bool delete;
184 struct udev_list_entry *item = NULL, *first = NULL;
185
186 assert(m);
187
7f275a9f
LP
188 if (!(sysfs = udev_device_get_syspath(dev)))
189 return -ENOMEM;
190
25ac040b
LP
191 /* Check whether this entry is even relevant for us. */
192 dn = udev_device_get_devnode(dev);
7f275a9f 193 expose = udev_device_get_property_value(dev, "SYSTEMD_EXPOSE");
25ac040b
LP
194 names = udev_device_get_property_value(dev, "SYSTEMD_NAMES");
195 wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS");
196
7f275a9f
LP
197 if (expose) {
198 int b;
199
200 if ((b = parse_boolean(expose)) < 0) {
201 log_error("Failed to parse SYSTEMD_EXPOSE udev property for device %s: %s", sysfs, expose);
202 return 0;
203 }
204
205 if (!b)
206 return 0;
207 } else
6326a423 208 if ((!dn || devnode_is_api(dn)) && !names && !wants)
7f275a9f 209 return 0;
25ac040b
LP
210
211 /* Ok, seems kinda interesting. Now, let's see if this one
212 * already exists. */
213
aab14b13
LP
214 if ((r = device_find_escape_name(m, sysfs, &u)) < 0)
215 return r;
25ac040b 216
aab14b13
LP
217 if (r == 0 && dn)
218 if ((r = device_find_escape_name(m, dn, &u)) < 0)
219 return r;
25ac040b 220
aab14b13
LP
221 if (r == 0) {
222 first = udev_device_get_devlinks_list_entry(dev);
223 udev_list_entry_foreach(item, first) {
224 if ((r = device_find_escape_name(m, udev_list_entry_get_name(item), &u)) < 0)
225 return r;
25ac040b 226
aab14b13
LP
227 if (r > 0)
228 break;
25ac040b 229 }
aab14b13
LP
230 }
231
232 /* FIXME: this needs proper merging */
233
234 assert((r > 0) == !!u);
235
236 /* If this is a different unit, then let's not merge things */
237 if (u && DEVICE(u)->sysfs && !streq(DEVICE(u)->sysfs, sysfs))
238 u = NULL;
25ac040b 239
aab14b13
LP
240 if (!u) {
241 delete = true;
242
243 if (!(u = unit_new(m)))
244 return -ENOMEM;
25ac040b 245
aab14b13 246 if ((r = device_add_escaped_name(u, sysfs, true)) < 0)
25ac040b
LP
247 goto fail;
248
e537352b
LP
249 unit_add_to_load_queue(u);
250 } else
251 delete = false;
252
253 if (!(DEVICE(u)->sysfs))
25ac040b
LP
254 if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
255 r = -ENOMEM;
256 goto fail;
257 }
258
25ac040b 259 if (dn)
2e478a46 260 if ((r = device_add_escaped_name(u, dn, true)) < 0)
25ac040b
LP
261 goto fail;
262
263 first = udev_device_get_devlinks_list_entry(dev);
264 udev_list_entry_foreach(item, first)
2e478a46 265 if ((r = device_add_escaped_name(u, udev_list_entry_get_name(item), false)) < 0)
25ac040b
LP
266 goto fail;
267
aab14b13
LP
268 if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
269 (model = udev_device_get_property_value(dev, "ID_MODEL"))) {
270 if ((r = unit_set_description(u, model)) < 0)
271 goto fail;
272 } else if (dn)
273 if ((r = unit_set_description(u, dn)) < 0)
274 goto fail;
275
276 /* We don't remove names that are gone. But that should be
277 * fine and should probably be fixed only on a configuration
278 * refresh. */
279
25ac040b
LP
280 if (names) {
281 FOREACH_WORD(w, l, names, state) {
aab14b13
LP
282 char *e;
283
b866264a
LP
284 if (!(e = strndup(w, l))) {
285 r = -ENOMEM;
25ac040b 286 goto fail;
b866264a 287 }
25ac040b
LP
288
289 r = unit_add_name(u, e);
290 free(e);
291
292 if (r < 0 && r != -EEXIST)
293 goto fail;
294 }
295 }
296
25ac040b
LP
297 if (wants) {
298 FOREACH_WORD(w, l, wants, state) {
aab14b13
LP
299 char *e;
300
b866264a
LP
301 if (!(e = strndup(w, l))) {
302 r = -ENOMEM;
25ac040b 303 goto fail;
b866264a 304 }
25ac040b
LP
305
306 r = unit_add_dependency_by_name(u, UNIT_WANTS, e);
307 free(e);
308
309 if (r < 0)
310 goto fail;
311 }
312 }
313
f94ea366
LP
314 if (update_state) {
315 manager_dispatch_load_queue(u->meta.manager);
316 device_set_state(DEVICE(u), DEVICE_AVAILABLE);
317 }
318
c1e1601e
LP
319 unit_add_to_dbus_queue(u);
320
25ac040b
LP
321 return 0;
322
323fail:
324 if (delete && u)
325 unit_free(u);
326 return r;
327}
328
f94ea366 329static int device_process_path(Manager *m, const char *path, bool update_state) {
25ac040b
LP
330 int r;
331 struct udev_device *dev;
332
333 assert(m);
334 assert(path);
335
336 if (!(dev = udev_device_new_from_syspath(m->udev, path))) {
337 log_warning("Failed to get udev device object from udev for path %s.", path);
338 return -ENOMEM;
339 }
340
f94ea366 341 r = device_process_new_device(m, dev, update_state);
25ac040b
LP
342 udev_device_unref(dev);
343 return r;
344}
345
f94ea366
LP
346static int device_process_removed_device(Manager *m, struct udev_device *dev) {
347 const char *sysfs;
348 char *e;
349 Unit *u;
350 Device *d;
351
352 assert(m);
353 assert(dev);
354
355 if (!(sysfs = udev_device_get_syspath(dev)))
356 return -ENOMEM;
357
358 assert(sysfs[0] == '/');
2e478a46 359 if (!(e = unit_name_escape_path(sysfs+1, ".device")))
f94ea366
LP
360 return -ENOMEM;
361
362 u = manager_get_unit(m, e);
363 free(e);
364
365 if (!u)
366 return 0;
367
368 d = DEVICE(u);
369 free(d->sysfs);
370 d->sysfs = NULL;
371
372 device_set_state(d, DEVICE_DEAD);
373 return 0;
374}
375
25ac040b
LP
376static void device_shutdown(Manager *m) {
377 assert(m);
378
f94ea366
LP
379 if (m->udev_monitor)
380 udev_monitor_unref(m->udev_monitor);
381
25ac040b
LP
382 if (m->udev)
383 udev_unref(m->udev);
384}
385
386static int device_enumerate(Manager *m) {
f94ea366 387 struct epoll_event ev;
25ac040b
LP
388 int r;
389 struct udev_enumerate *e = NULL;
390 struct udev_list_entry *item = NULL, *first = NULL;
391
392 assert(m);
393
394 if (!(m->udev = udev_new()))
395 return -ENOMEM;
396
f94ea366
LP
397 if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
398 r = -ENOMEM;
399 goto fail;
400 }
401
402 if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
403 r = -EIO;
404 goto fail;
405 }
406
407 m->udev_watch.type = WATCH_UDEV;
408 m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
409
410 zero(ev);
411 ev.events = EPOLLIN;
412 ev.data.ptr = &m->udev_watch;
413
414 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
415 return -errno;
416
25ac040b
LP
417 if (!(e = udev_enumerate_new(m->udev))) {
418 r = -ENOMEM;
419 goto fail;
420 }
421
422 if (udev_enumerate_scan_devices(e) < 0) {
423 r = -EIO;
424 goto fail;
425 }
426
427 first = udev_enumerate_get_list_entry(e);
428 udev_list_entry_foreach(item, first)
f94ea366 429 device_process_path(m, udev_list_entry_get_name(item), false);
25ac040b
LP
430
431 udev_enumerate_unref(e);
25ac040b
LP
432 return 0;
433
434fail:
435 if (e)
436 udev_enumerate_unref(e);
437
438 device_shutdown(m);
439 return r;
5cb5a6ff
LP
440}
441
f94ea366
LP
442void device_fd_event(Manager *m, int events) {
443 struct udev_device *dev;
444 int r;
445 const char *action;
446
447 assert(m);
448 assert(events == EPOLLIN);
449
f94ea366
LP
450 if (!(dev = udev_monitor_receive_device(m->udev_monitor))) {
451 log_error("Failed to receive device.");
452 return;
453 }
454
455 if (!(action = udev_device_get_action(dev))) {
456 log_error("Failed to get udev action string.");
457 goto fail;
458 }
459
460 if (streq(action, "remove")) {
461 if ((r = device_process_removed_device(m, dev)) < 0) {
462 log_error("Failed to process udev device event: %s", strerror(-r));
463 goto fail;
464 }
465 } else {
466 if ((r = device_process_new_device(m, dev, true)) < 0) {
467 log_error("Failed to process udev device event: %s", strerror(-r));
468 goto fail;
469 }
470 }
471
472fail:
473 udev_device_unref(dev);
474}
475
87f0e418 476const UnitVTable device_vtable = {
5cb5a6ff
LP
477 .suffix = ".device",
478
e537352b
LP
479 .init = device_init,
480 .load = unit_load_fragment_and_dropin_optional,
034c6ed7 481 .done = device_done,
f50e0a01
LP
482 .coldplug = device_coldplug,
483
5cb5a6ff
LP
484 .dump = device_dump,
485
f50e0a01 486 .active_state = device_active_state,
10a94420 487 .sub_state_to_string = device_sub_state_to_string,
25ac040b 488
f50e0a01
LP
489 .enumerate = device_enumerate,
490 .shutdown = device_shutdown
5cb5a6ff 491};