chiark / gitweb /
34d760a48a55930982958aa3a129cf8c3de86e31
[elogind.git] / device.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <errno.h>
4 #include <libudev.h>
5
6 #include "unit.h"
7 #include "device.h"
8 #include "strv.h"
9 #include "log.h"
10
11 static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
12         [DEVICE_DEAD] = UNIT_INACTIVE,
13         [DEVICE_AVAILABLE] = UNIT_ACTIVE
14 };
15
16 static const char* const state_string_table[_DEVICE_STATE_MAX] = {
17         [DEVICE_DEAD] = "dead",
18         [DEVICE_AVAILABLE] = "available"
19 };
20
21 static void device_done(Unit *u) {
22         Device *d = DEVICE(u);
23
24         assert(d);
25         free(d->sysfs);
26 }
27
28 static void device_set_state(Device *d, DeviceState state) {
29         DeviceState old_state;
30         assert(d);
31
32         old_state = d->state;
33         d->state = state;
34
35         log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]);
36
37         unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
38 }
39
40 static int device_coldplug(Unit *u) {
41         Device *d = DEVICE(u);
42
43         assert(d);
44         assert(d->state == DEVICE_DEAD);
45
46         if (d->sysfs)
47                 device_set_state(d, DEVICE_AVAILABLE);
48
49         return 0;
50 }
51
52 static void device_dump(Unit *u, FILE *f, const char *prefix) {
53         Device *d = DEVICE(u);
54
55         assert(d);
56
57         fprintf(f,
58                 "%sDevice State: %s\n"
59                 "%sSysfs Path: %s\n",
60                 prefix, state_string_table[d->state],
61                 prefix, strna(d->sysfs));
62 }
63
64 static UnitActiveState device_active_state(Unit *u) {
65         assert(u);
66
67         return state_translation_table[DEVICE(u)->state];
68 }
69
70 static int device_add_escaped_name(Unit *u, const char *prefix, const char *dn, bool make_id) {
71         char *e;
72         int r;
73
74         assert(u);
75         assert(dn);
76         assert(dn[0] == '/');
77
78         if (!(e = unit_name_escape_path(prefix, dn+1, ".device")))
79                 return -ENOMEM;
80
81         r = unit_add_name(u, e);
82
83         if (r >= 0 && make_id)
84                 unit_choose_id(u, e);
85
86         free(e);
87
88         if (r < 0 && r != -EEXIST)
89                 return r;
90
91         return 0;
92 }
93
94 static int device_process_device(Manager *m, struct udev_device *dev) {
95         const char *dn, *names, *wants, *sysfs;
96         Unit *u = NULL;
97         int r;
98         char *e, *w, *state;
99         size_t l;
100         bool delete;
101         struct udev_list_entry *item = NULL, *first = NULL;
102
103         assert(m);
104
105         /* Check whether this entry is even relevant for us. */
106         dn = udev_device_get_devnode(dev);
107         names = udev_device_get_property_value(dev, "SYSTEMD_NAMES");
108         wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS");
109
110         if (!dn && !names && !wants)
111                 return 0;
112
113         /* Ok, seems kinda interesting. Now, let's see if this one
114          * already exists. */
115
116         if (!(sysfs = udev_device_get_syspath(dev)))
117                 return -ENOMEM;
118
119         assert(sysfs[0] == '/');
120         if (!(e = unit_name_escape_path("sysfs-", sysfs+1, ".device")))
121                 return -ENOMEM;
122
123         if (!(u = manager_get_unit(m, e))) {
124                 const char *model;
125
126                 delete = true;
127
128                 if (!(u = unit_new(m))) {
129                         free(e);
130                         return -ENOMEM;
131                 }
132
133                 r = unit_add_name(u, e);
134                 free(e);
135
136                 if (r < 0)
137                         goto fail;
138
139                 if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
140                         r = -ENOMEM;
141                         goto fail;
142                 }
143
144                 if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
145                     (model = udev_device_get_property_value(dev, "ID_MODEL")))
146                         if ((r = unit_set_description(u, model)) < 0)
147                                 goto fail;
148
149         } else {
150                 delete = false;
151                 free(e);
152         }
153
154         if (dn)
155                 if ((r = device_add_escaped_name(u, "node-", dn, true)) < 0)
156                         goto fail;
157
158         first = udev_device_get_devlinks_list_entry(dev);
159         udev_list_entry_foreach(item, first)
160                 if ((r = device_add_escaped_name(u, "node-", udev_list_entry_get_name(item), false)) < 0)
161                         goto fail;
162
163         if (names) {
164                 FOREACH_WORD(w, l, names, state) {
165                         if (!(e = strndup(w, l)))
166                                 goto fail;
167
168                         r = unit_add_name(u, e);
169                         free(e);
170
171                         if (r < 0 && r != -EEXIST)
172                                 goto fail;
173                 }
174         }
175
176
177         if (set_isempty(u->meta.names)) {
178                 r = -EEXIST;
179                 goto fail;
180         }
181
182         if (wants) {
183                 FOREACH_WORD(w, l, wants, state) {
184                         if (!(e = strndup(w, l)))
185                                 goto fail;
186
187                         r = unit_add_dependency_by_name(u, UNIT_WANTS, e);
188                         free(e);
189
190                         if (r < 0)
191                                 goto fail;
192                 }
193         }
194
195         unit_add_to_load_queue(u);
196         return 0;
197
198 fail:
199         if (delete && u)
200                 unit_free(u);
201         return r;
202 }
203
204 static int device_process_path(Manager *m, const char *path) {
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
216         r = device_process_device(m, dev);
217         udev_device_unref(dev);
218         return r;
219 }
220
221 static void device_shutdown(Manager *m) {
222         assert(m);
223
224         if (m->udev)
225                 udev_unref(m->udev);
226 }
227
228 static int device_enumerate(Manager *m) {
229         int r;
230         struct udev_enumerate *e = NULL;
231         struct udev_list_entry *item = NULL, *first = NULL;
232
233         assert(m);
234
235         if (!(m->udev = udev_new()))
236                 return -ENOMEM;
237
238         if (!(e = udev_enumerate_new(m->udev))) {
239                 r = -ENOMEM;
240                 goto fail;
241         }
242
243         if (udev_enumerate_scan_devices(e) < 0) {
244                 r = -EIO;
245                 goto fail;
246         }
247
248         first = udev_enumerate_get_list_entry(e);
249         udev_list_entry_foreach(item, first)
250                 device_process_path(m, udev_list_entry_get_name(item));
251
252         udev_enumerate_unref(e);
253
254         return 0;
255
256 fail:
257         if (e)
258                 udev_enumerate_unref(e);
259
260         device_shutdown(m);
261         return r;
262 }
263
264 const UnitVTable device_vtable = {
265         .suffix = ".device",
266
267         .init = unit_load_fragment_and_dropin,
268         .done = device_done,
269         .coldplug = device_coldplug,
270
271         .dump = device_dump,
272
273         .active_state = device_active_state,
274
275         .enumerate = device_enumerate,
276         .shutdown = device_shutdown
277 };