]> git.ipfire.org Git - people/ms/systemd.git/blame - device.c
Merge remote branch 'kay/master'
[people/ms/systemd.git] / device.c
CommitLineData
5cb5a6ff
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
2
25ac040b 3#include <errno.h>
f94ea366 4#include <sys/epoll.h>
25ac040b
LP
5#include <libudev.h>
6
87f0e418 7#include "unit.h"
5cb5a6ff
LP
8#include "device.h"
9#include "strv.h"
25ac040b 10#include "log.h"
5cb5a6ff 11
f50e0a01
LP
12static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
13 [DEVICE_DEAD] = UNIT_INACTIVE,
14 [DEVICE_AVAILABLE] = UNIT_ACTIVE
15};
16
17static const char* const state_string_table[_DEVICE_STATE_MAX] = {
18 [DEVICE_DEAD] = "dead",
19 [DEVICE_AVAILABLE] = "available"
20};
21
87f0e418
LP
22static void device_done(Unit *u) {
23 Device *d = DEVICE(u);
034c6ed7
LP
24
25 assert(d);
25ac040b 26 free(d->sysfs);
034c6ed7
LP
27}
28
f50e0a01
LP
29static void device_set_state(Device *d, DeviceState state) {
30 DeviceState old_state;
31 assert(d);
5cb5a6ff 32
f50e0a01
LP
33 old_state = d->state;
34 d->state = state;
5cb5a6ff 35
f50e0a01
LP
36 log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]);
37
38 unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
39}
40
41static int device_coldplug(Unit *u) {
42 Device *d = DEVICE(u);
43
44 assert(d);
45 assert(d->state == DEVICE_DEAD);
46
47 if (d->sysfs)
48 device_set_state(d, DEVICE_AVAILABLE);
49
50 return 0;
51}
52
53static void device_dump(Unit *u, FILE *f, const char *prefix) {
25ac040b 54 Device *d = DEVICE(u);
5cb5a6ff 55
25ac040b 56 assert(d);
5cb5a6ff
LP
57
58 fprintf(f,
25ac040b
LP
59 "%sDevice State: %s\n"
60 "%sSysfs Path: %s\n",
f50e0a01
LP
61 prefix, state_string_table[d->state],
62 prefix, strna(d->sysfs));
63}
64
65static UnitActiveState device_active_state(Unit *u) {
66 assert(u);
67
68 return state_translation_table[DEVICE(u)->state];
25ac040b
LP
69}
70
b08d03ff 71static int device_add_escaped_name(Unit *u, const char *prefix, const char *dn, bool make_id) {
25ac040b
LP
72 char *e;
73 int r;
74
75 assert(u);
76 assert(dn);
77 assert(dn[0] == '/');
78
b08d03ff 79 if (!(e = unit_name_escape_path(prefix, dn+1, ".device")))
25ac040b
LP
80 return -ENOMEM;
81
82 r = unit_add_name(u, e);
b08d03ff
LP
83
84 if (r >= 0 && make_id)
85 unit_choose_id(u, e);
86
25ac040b
LP
87 free(e);
88
89 if (r < 0 && r != -EEXIST)
90 return r;
91
92 return 0;
93}
94
f94ea366 95static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
25ac040b
LP
96 const char *dn, *names, *wants, *sysfs;
97 Unit *u = NULL;
98 int r;
99 char *e, *w, *state;
100 size_t l;
101 bool delete;
102 struct udev_list_entry *item = NULL, *first = NULL;
103
104 assert(m);
105
106 /* Check whether this entry is even relevant for us. */
107 dn = udev_device_get_devnode(dev);
108 names = udev_device_get_property_value(dev, "SYSTEMD_NAMES");
109 wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS");
110
111 if (!dn && !names && !wants)
112 return 0;
113
114 /* Ok, seems kinda interesting. Now, let's see if this one
115 * already exists. */
116
117 if (!(sysfs = udev_device_get_syspath(dev)))
118 return -ENOMEM;
119
120 assert(sysfs[0] == '/');
b08d03ff 121 if (!(e = unit_name_escape_path("sysfs-", sysfs+1, ".device")))
25ac040b
LP
122 return -ENOMEM;
123
124 if (!(u = manager_get_unit(m, e))) {
125 const char *model;
126
127 delete = true;
128
129 if (!(u = unit_new(m))) {
130 free(e);
131 return -ENOMEM;
132 }
133
134 r = unit_add_name(u, e);
135 free(e);
136
137 if (r < 0)
138 goto fail;
139
140 if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
141 r = -ENOMEM;
142 goto fail;
143 }
144
145 if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
f50e0a01
LP
146 (model = udev_device_get_property_value(dev, "ID_MODEL")))
147 if ((r = unit_set_description(u, model)) < 0)
25ac040b 148 goto fail;
25ac040b 149
f94ea366 150 unit_add_to_load_queue(u);
25ac040b
LP
151 } else {
152 delete = false;
153 free(e);
154 }
155
156 if (dn)
b08d03ff 157 if ((r = device_add_escaped_name(u, "node-", dn, true)) < 0)
25ac040b
LP
158 goto fail;
159
160 first = udev_device_get_devlinks_list_entry(dev);
161 udev_list_entry_foreach(item, first)
b08d03ff 162 if ((r = device_add_escaped_name(u, "node-", udev_list_entry_get_name(item), false)) < 0)
25ac040b
LP
163 goto fail;
164
165 if (names) {
166 FOREACH_WORD(w, l, names, state) {
167 if (!(e = strndup(w, l)))
168 goto fail;
169
170 r = unit_add_name(u, e);
171 free(e);
172
173 if (r < 0 && r != -EEXIST)
174 goto fail;
175 }
176 }
177
25ac040b
LP
178 if (wants) {
179 FOREACH_WORD(w, l, wants, state) {
180 if (!(e = strndup(w, l)))
181 goto fail;
182
183 r = unit_add_dependency_by_name(u, UNIT_WANTS, e);
184 free(e);
185
186 if (r < 0)
187 goto fail;
188 }
189 }
190
f94ea366
LP
191 if (update_state) {
192 manager_dispatch_load_queue(u->meta.manager);
193 device_set_state(DEVICE(u), DEVICE_AVAILABLE);
194 }
195
25ac040b
LP
196 return 0;
197
198fail:
199 if (delete && u)
200 unit_free(u);
201 return r;
202}
203
f94ea366 204static int device_process_path(Manager *m, const char *path, bool update_state) {
25ac040b
LP
205 int r;
206 struct udev_device *dev;
207
208 assert(m);
209 assert(path);
210
211 if (!(dev = udev_device_new_from_syspath(m->udev, path))) {
212 log_warning("Failed to get udev device object from udev for path %s.", path);
213 return -ENOMEM;
214 }
215
f94ea366 216 r = device_process_new_device(m, dev, update_state);
25ac040b
LP
217 udev_device_unref(dev);
218 return r;
219}
220
f94ea366
LP
221static int device_process_removed_device(Manager *m, struct udev_device *dev) {
222 const char *sysfs;
223 char *e;
224 Unit *u;
225 Device *d;
226
227 assert(m);
228 assert(dev);
229
230 if (!(sysfs = udev_device_get_syspath(dev)))
231 return -ENOMEM;
232
233 assert(sysfs[0] == '/');
234 if (!(e = unit_name_escape_path("sysfs-", sysfs+1, ".device")))
235 return -ENOMEM;
236
237 u = manager_get_unit(m, e);
238 free(e);
239
240 if (!u)
241 return 0;
242
243 d = DEVICE(u);
244 free(d->sysfs);
245 d->sysfs = NULL;
246
247 device_set_state(d, DEVICE_DEAD);
248 return 0;
249}
250
25ac040b
LP
251static void device_shutdown(Manager *m) {
252 assert(m);
253
f94ea366
LP
254 if (m->udev_monitor)
255 udev_monitor_unref(m->udev_monitor);
256
25ac040b
LP
257 if (m->udev)
258 udev_unref(m->udev);
259}
260
261static int device_enumerate(Manager *m) {
f94ea366 262 struct epoll_event ev;
25ac040b
LP
263 int r;
264 struct udev_enumerate *e = NULL;
265 struct udev_list_entry *item = NULL, *first = NULL;
266
267 assert(m);
268
269 if (!(m->udev = udev_new()))
270 return -ENOMEM;
271
f94ea366
LP
272 if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
273 r = -ENOMEM;
274 goto fail;
275 }
276
277 if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
278 r = -EIO;
279 goto fail;
280 }
281
282 m->udev_watch.type = WATCH_UDEV;
283 m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
284
285 zero(ev);
286 ev.events = EPOLLIN;
287 ev.data.ptr = &m->udev_watch;
288
289 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
290 return -errno;
291
25ac040b
LP
292 if (!(e = udev_enumerate_new(m->udev))) {
293 r = -ENOMEM;
294 goto fail;
295 }
296
297 if (udev_enumerate_scan_devices(e) < 0) {
298 r = -EIO;
299 goto fail;
300 }
301
302 first = udev_enumerate_get_list_entry(e);
303 udev_list_entry_foreach(item, first)
f94ea366 304 device_process_path(m, udev_list_entry_get_name(item), false);
25ac040b
LP
305
306 udev_enumerate_unref(e);
25ac040b
LP
307 return 0;
308
309fail:
310 if (e)
311 udev_enumerate_unref(e);
312
313 device_shutdown(m);
314 return r;
5cb5a6ff
LP
315}
316
f94ea366
LP
317void device_fd_event(Manager *m, int events) {
318 struct udev_device *dev;
319 int r;
320 const char *action;
321
322 assert(m);
323 assert(events == EPOLLIN);
324
325 log_debug("got udev event");
326
327 if (!(dev = udev_monitor_receive_device(m->udev_monitor))) {
328 log_error("Failed to receive device.");
329 return;
330 }
331
332 if (!(action = udev_device_get_action(dev))) {
333 log_error("Failed to get udev action string.");
334 goto fail;
335 }
336
337 if (streq(action, "remove")) {
338 if ((r = device_process_removed_device(m, dev)) < 0) {
339 log_error("Failed to process udev device event: %s", strerror(-r));
340 goto fail;
341 }
342 } else {
343 if ((r = device_process_new_device(m, dev, true)) < 0) {
344 log_error("Failed to process udev device event: %s", strerror(-r));
345 goto fail;
346 }
347 }
348
349fail:
350 udev_device_unref(dev);
351}
352
87f0e418 353const UnitVTable device_vtable = {
5cb5a6ff
LP
354 .suffix = ".device",
355
87f0e418 356 .init = unit_load_fragment_and_dropin,
034c6ed7 357 .done = device_done,
f50e0a01
LP
358 .coldplug = device_coldplug,
359
5cb5a6ff
LP
360 .dump = device_dump,
361
f50e0a01 362 .active_state = device_active_state,
25ac040b 363
f50e0a01
LP
364 .enumerate = device_enumerate,
365 .shutdown = device_shutdown
5cb5a6ff 366};