discover/network: Send DHCP client architecture type
[petitboot] / discover / udev.c
1
2 #if defined(HAVE_CONFIG_H)
3 #include "config.h"
4 #endif
5
6 #include <assert.h>
7 #include <errno.h>
8 #include <libudev.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <sys/socket.h>
14 #include <sys/types.h>
15 #include <sys/un.h>
16
17 #include <log/log.h>
18 #include <types/types.h>
19 #include <talloc/talloc.h>
20 #include <waiter/waiter.h>
21 #include <system/system.h>
22
23 #include "event.h"
24 #include "udev.h"
25 #include "pb-discover.h"
26 #include "device-handler.h"
27 #include "cdrom.h"
28
29 struct pb_udev {
30         struct udev *udev;
31         struct udev_monitor *monitor;
32         struct device_handler *handler;
33 };
34
35 static int udev_destructor(void *p)
36 {
37         struct pb_udev *udev = p;
38
39         udev_monitor_unref(udev->monitor);
40         udev->monitor = NULL;
41
42         udev_unref(udev->udev);
43         udev->udev = NULL;
44
45         return 0;
46 }
47
48 static void udev_setup_device_params(struct udev_device *udev,
49                 struct discover_device *dev)
50 {
51         struct udev_list_entry *list, *entry;
52
53         list = udev_device_get_properties_list_entry(udev);
54         if (!list)
55                 return;
56
57         udev_list_entry_foreach(entry, list)
58                 discover_device_set_param(dev,
59                                 udev_list_entry_get_name(entry),
60                                 udev_list_entry_get_value(entry));
61 }
62
63 static int udev_handle_dev_add(struct pb_udev *udev, struct udev_device *dev)
64 {
65         struct discover_device *ddev;
66         const char *typestr;
67         const char *uuid;
68         const char *path;
69         const char *name;
70         const char *node;
71         const char *prop;
72         bool cdrom;
73
74         name = udev_device_get_sysname(dev);
75         if (!name) {
76                 pb_debug("udev_device_get_sysname failed\n");
77                 return -1;
78         }
79
80         typestr = udev_device_get_devtype(dev);
81         if (!typestr) {
82                 pb_debug("udev_device_get_devtype failed\n");
83                 return -1;
84         }
85
86         if (!(!strcmp(typestr, "disk") || !strcmp(typestr, "partition"))) {
87                 pb_debug("SKIP %s: invalid type %s\n", name, typestr);
88                 return 0;
89         }
90
91         node = udev_device_get_devnode(dev);
92         path = udev_device_get_devpath(dev);
93         if (path && (strstr(path, "virtual/block/loop")
94                         || strstr(path, "virtual/block/ram"))) {
95                 pb_debug("SKIP: %s: ignored (path=%s)\n", name, path);
96                 return 0;
97         }
98
99         cdrom = node && !!udev_device_get_property_value(dev, "ID_CDROM");
100         if (cdrom) {
101                 /* CDROMs require a little initialisation, to get
102                  * petitboot-compatible tray behaviour */
103                 cdrom_init(node);
104                 if (!cdrom_media_present(node)) {
105                         pb_debug("SKIP: %s: no media present\n", name);
106                         return 0;
107                 }
108         }
109
110         /* We have enough info to create the device and start discovery */
111         ddev = device_lookup_by_id(udev->handler, name);
112         if (ddev) {
113                 pb_debug("device %s is already present?\n", name);
114                 return -1;
115         }
116
117         /* We may see multipath devices; they'll have the same uuid as an
118          * existing device, so only parse the first. */
119         uuid = udev_device_get_property_value(dev, "ID_FS_UUID");
120         if (uuid) {
121                 ddev = device_lookup_by_uuid(udev->handler, uuid);
122                 if (ddev) {
123                         pb_log("SKIP: %s UUID [%s] already present (as %s)\n",
124                                         name, uuid, ddev->device->id);
125                         return -1;
126                 }
127         }
128
129         ddev = discover_device_create(udev->handler, name);
130
131         ddev->device_path = talloc_strdup(ddev, node);
132
133         if (uuid)
134                 ddev->uuid = talloc_strdup(ddev, uuid);
135         prop = udev_device_get_property_value(dev, "ID_FS_LABEL");
136         if (prop)
137                 ddev->label = talloc_strdup(ddev, prop);
138         ddev->device->type = cdrom ? DEVICE_TYPE_OPTICAL : DEVICE_TYPE_DISK;
139
140         udev_setup_device_params(dev, ddev);
141
142         device_handler_discover(udev->handler, ddev);
143
144         return 0;
145 }
146
147 static int udev_handle_dev_remove(struct pb_udev *udev, struct udev_device *dev)
148 {
149         struct discover_device *ddev;
150         const char *name;
151
152         name = udev_device_get_sysname(dev);
153         if (!name) {
154                 pb_debug("udev_device_get_sysname failed\n");
155                 return -1;
156         }
157
158         ddev = device_lookup_by_id(udev->handler, name);
159         if (!ddev)
160                 return 0;
161
162         device_handler_remove(udev->handler, ddev);
163
164         return 0;
165 }
166
167 static int udev_handle_dev_change(struct pb_udev *udev, struct udev_device *dev)
168 {
169         struct discover_device *ddev;
170         const char *name, *node;
171
172         name = udev_device_get_sysname(dev);
173         node = udev_device_get_devnode(dev);
174
175         /* we're only interested in CDROM change events at present */
176         if (!udev_device_get_property_value(dev, "ID_CDROM"))
177                 return 0;
178
179         /* handle CDROM eject requests */
180         if (udev_device_get_property_value(dev, "DISK_EJECT_REQUEST")) {
181                 bool eject = false;
182
183                 pb_debug("udev: eject request\n");
184
185                 /* If the device is mounted, cdrom_id's own eject request may
186                  * have failed. So, we'll need to do our own here.
187                  */
188                 ddev = device_lookup_by_id(udev->handler, name);
189                 if (ddev) {
190                         eject = ddev->mounted;
191                         udev_handle_dev_remove(udev, dev);
192                 }
193
194                 if (eject)
195                         cdrom_eject(node);
196
197                 return 0;
198         }
199
200         if (udev_device_get_property_value(dev, "DISK_MEDIA_CHANGE")) {
201                 if (cdrom_media_present(node))
202                         return udev_handle_dev_add(udev, dev);
203                 else
204                         return udev_handle_dev_remove(udev, dev);
205         }
206
207         return 0;
208 }
209
210 static int udev_handle_dev_action(struct udev_device *dev, const char *action)
211 {
212         struct pb_udev *udev = udev_get_userdata(udev_device_get_udev(dev));
213
214 #ifdef DEBUG
215         {
216                 struct udev_list_entry *list;
217                 const char *name;
218
219                 list = udev_device_get_properties_list_entry(dev);
220                 name = udev_device_get_sysname(dev);
221
222                 pb_debug("%s: action %s, device %s\n", __func__, action, name);
223                 pb_debug("%s properties:\n", __func__);
224
225                 for (; list; list = udev_list_entry_get_next(list))
226                         pb_log("\t%-20s: %s\n", udev_list_entry_get_name(list),
227                                         udev_list_entry_get_value(list));
228         } while (0);
229 #endif
230
231         if (!strcmp(action, "add"))
232                 return udev_handle_dev_add(udev, dev);
233
234         else if (!strcmp(action, "remove"))
235                 return udev_handle_dev_remove(udev, dev);
236
237         else if (!strcmp(action, "change"))
238                 return udev_handle_dev_change(udev, dev);
239
240         return 0;
241 }
242
243 static int udev_enumerate(struct udev *udev)
244 {
245         int result;
246         struct udev_list_entry *list, *entry;
247         struct udev_enumerate *enumerate;
248
249         enumerate = udev_enumerate_new(udev);
250
251         if (!enumerate) {
252                 pb_log("udev_enumerate_new failed\n");
253                 return -1;
254         }
255
256         result = udev_enumerate_add_match_subsystem(enumerate, "block");
257
258         if (result) {
259                 pb_log("udev_enumerate_add_match_subsystem failed\n");
260                 goto fail;
261         }
262
263         udev_enumerate_scan_devices(enumerate);
264
265         list = udev_enumerate_get_list_entry(enumerate);
266
267         if (!list) {
268                 pb_log("udev_enumerate_get_list_entry failed\n");
269                 goto fail;
270         }
271
272         udev_list_entry_foreach(entry, list) {
273                 const char *syspath;
274                 struct udev_device *dev;
275
276                 syspath = udev_list_entry_get_name(entry);
277                 dev = udev_device_new_from_syspath(udev, syspath);
278
279                 udev_handle_dev_action(dev, "add");
280
281                 udev_device_unref(dev);
282         }
283
284         udev_enumerate_unref(enumerate);
285         return 0;
286
287 fail:
288         udev_enumerate_unref(enumerate);
289         return -1;
290 }
291
292 static int udev_setup_monitor(struct udev *udev, struct udev_monitor **monitor)
293 {
294         int result;
295         struct udev_monitor *m;
296
297         *monitor = NULL;
298         m = udev_monitor_new_from_netlink(udev, "udev");
299
300         if (!m) {
301                 pb_log("udev_monitor_new_from_netlink failed\n");
302                 goto out_err;
303         }
304
305         result = udev_monitor_filter_add_match_subsystem_devtype(m, "block",
306                 NULL);
307
308         if (result) {
309                 pb_log("udev_monitor_filter_add_match_subsystem_devtype failed\n");
310                 goto out_err;
311         }
312
313         result = udev_monitor_enable_receiving(m);
314
315         if (result) {
316                 pb_log("udev_monitor_enable_receiving failed\n");
317                 goto out_err;
318         }
319
320         *monitor = m;
321         return 0;
322
323 out_err:
324         udev_monitor_unref(m);
325         return -1;
326 }
327
328 /*
329  * udev_process - waiter callback for monitor netlink.
330  */
331
332 static int udev_process(void *arg)
333 {
334         struct udev_monitor *monitor = arg;
335         struct udev_device *dev;
336         const char *action;
337         int result;
338
339         dev = udev_monitor_receive_device(monitor);
340
341         if (!dev) {
342                 pb_log("udev_monitor_receive_device failed\n");
343                 return -1;
344         }
345
346         action = udev_device_get_action(dev);
347
348         if (!action) {
349                 pb_log("udev_device_get_action failed\n");
350                 goto fail;
351         }
352
353         result = udev_handle_dev_action(dev, action);
354
355         udev_device_unref(dev);
356         return result;
357
358 fail:
359         udev_device_unref(dev);
360         return -1;
361 }
362
363 static void udev_log_fn(struct udev __attribute__((unused)) *udev,
364         int __attribute__((unused)) priority, const char *file, int line,
365         const char *fn, const char *format, va_list args)
366 {
367       pb_log("libudev: %s %s:%d: ", fn, file, line);
368       vfprintf(pb_log_get_stream(), format, args);
369 }
370
371 struct pb_udev *udev_init(struct waitset *waitset,
372         struct device_handler *handler)
373 {
374         int result;
375         struct pb_udev *udev = talloc(NULL, struct pb_udev);
376
377         talloc_set_destructor(udev, udev_destructor);
378         udev->handler = handler;
379
380         udev->udev = udev_new();
381
382         if (!udev->udev) {
383                 pb_log("udev_new failed\n");
384                 goto fail_new;
385         }
386
387         udev_set_userdata(udev->udev, udev);
388
389         udev_set_log_fn(udev->udev, udev_log_fn);
390
391         result = udev_enumerate(udev->udev);
392
393         if (result)
394                 goto fail_enumerate;
395
396         result = udev_setup_monitor(udev->udev, &udev->monitor);
397
398         if (result)
399                 goto fail_monitor;
400
401         waiter_register_io(waitset, udev_monitor_get_fd(udev->monitor), WAIT_IN,
402                 udev_process, udev->monitor);
403
404         pb_debug("%s: waiting on udev\n", __func__);
405
406         return udev;
407
408 fail_monitor:
409 fail_enumerate:
410         udev_unref(udev->udev);
411 fail_new:
412         talloc_free(udev);
413         return NULL;
414 }
415
416 void udev_destroy(struct pb_udev *udev)
417 {
418         talloc_free(udev);
419 }