]> git.ozlabs.org Git - petitboot/blob - discover/udev.c
discover/udev: Handle LVM logical volumes
[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 #include <process/process.h>
23
24 #include "event.h"
25 #include "udev.h"
26 #include "pb-discover.h"
27 #include "device-handler.h"
28 #include "cdrom.h"
29 #include "devmapper.h"
30
31 /* We set a default monitor buffer size, as we may not process monitor
32  * events while performing device discvoery. systemd uses a 128M buffer, so
33  * we'll do the same here */
34 static const int monitor_bufsize = 128 * 1024 * 1024;
35
36 struct pb_udev {
37         struct udev *udev;
38         struct udev_monitor *monitor;
39         struct device_handler *handler;
40 };
41
42 static int udev_destructor(void *p)
43 {
44         struct pb_udev *udev = p;
45
46         if (udev->monitor) {
47                 udev_monitor_unref(udev->monitor);
48                 udev->monitor = NULL;
49         }
50
51         if (udev->udev) {
52                 udev_unref(udev->udev);
53                 udev->udev = NULL;
54         }
55
56         return 0;
57 }
58
59 static void udev_setup_device_params(struct udev_device *udev,
60                 struct discover_device *dev)
61 {
62         struct udev_list_entry *list, *entry;
63
64         list = udev_device_get_properties_list_entry(udev);
65         if (!list)
66                 return;
67
68         udev_list_entry_foreach(entry, list)
69                 discover_device_set_param(dev,
70                                 udev_list_entry_get_name(entry),
71                                 udev_list_entry_get_value(entry));
72 }
73
74 /*
75  * Search for LVM logical volumes. If any exist they should be recognised
76  * by udev as normal.
77  * Normally this is handled in an init script, but on some platforms
78  * disks are slow enough to come up that we need to check again.
79  */
80 static void lvm_vg_search(struct device_handler *handler)
81 {
82         if (process_run_simple(handler, pb_system_apps.vgscan, "-qq", NULL))
83                 pb_log("%s: Failed to execute vgscan\n", __func__);
84
85         if (process_run_simple(handler, pb_system_apps.vgchange, "-ay", "-qq",
86                                 NULL))
87                 pb_log("%s: Failed to execute vgchange\n", __func__);
88 }
89
90 static int udev_handle_block_add(struct pb_udev *udev, struct udev_device *dev,
91                 const char *name)
92 {
93         char *devlinks = NULL, *link, *saveptr = NULL;
94         struct discover_device *ddev;
95         unsigned int i = 0;
96         const char *typestr;
97         const char *uuid;
98         const char *path;
99         const char *node;
100         const char *prop;
101         const char *type;
102         const char *devname;
103         const char *ignored_types[] = {
104                 "linux_raid_member",
105                 "swap",
106                 NULL,
107         };
108         bool cdrom, usb;
109
110         typestr = udev_device_get_devtype(dev);
111         if (!typestr) {
112                 pb_debug("udev_device_get_devtype failed\n");
113                 return -1;
114         }
115
116         if (!(!strcmp(typestr, "disk") || !strcmp(typestr, "partition"))) {
117                 pb_log("SKIP %s: invalid type %s\n", name, typestr);
118                 return 0;
119         }
120
121         node = udev_device_get_devnode(dev);
122         path = udev_device_get_devpath(dev);
123         if (path && strstr(path, "virtual/block/loop")) {
124                 pb_log("SKIP: %s: ignored (path=%s)\n", name, path);
125                 return 0;
126         }
127
128         if (path && strstr(path, "virtual/block/ram")) {
129                 device_handler_add_ramdisk(udev->handler, node);
130                 return 0;
131         }
132
133         cdrom = node && !!udev_device_get_property_value(dev, "ID_CDROM");
134         if (cdrom) {
135                 /* CDROMs require a little initialisation, to get
136                  * petitboot-compatible tray behaviour */
137                 cdrom_init(node);
138                 if (!cdrom_media_present(node)) {
139                         pb_log("SKIP: %s: no media present\n", name);
140                         return 0;
141                 }
142         }
143
144         /* Ignore any device mapper devices that aren't logical volumes */
145         devname = udev_device_get_property_value(dev, "DM_NAME");
146         if (devname && ! udev_device_get_property_value(dev, "DM_LV_NAME")) {
147                 pb_debug("SKIP: dm-device %s\n", devname);
148                 return 0;
149         }
150
151         type = udev_device_get_property_value(dev, "ID_FS_TYPE");
152         if (!type) {
153                 pb_log("SKIP: %s: no ID_FS_TYPE property\n", name);
154                 return 0;
155         }
156
157         while (ignored_types[i]) {
158                 if (!strncmp(type, ignored_types[i], strlen(ignored_types[i]))) {
159                         pb_log("SKIP: %s: ignore '%s' filesystem\n", name, type);
160                         return 0;
161                 }
162                 i++;
163         }
164
165         /* Search for LVM logical volumes if we see an LVM member */
166         if (strncmp(type, "LVM2_member", strlen("LVM2_member")) == 0) {
167                 lvm_vg_search(udev->handler);
168                 return 0;
169         }
170
171         /* We may see multipath devices; they'll have the same uuid as an
172          * existing device, so only parse the first. */
173         uuid = udev_device_get_property_value(dev, "ID_FS_UUID");
174         if (uuid) {
175                 ddev = device_lookup_by_uuid(udev->handler, uuid);
176                 if (ddev) {
177                         pb_log("SKIP: %s UUID [%s] already present (as %s)\n",
178                                         name, uuid, ddev->device->id);
179                         return 0;
180                 }
181         }
182
183         /* Use DM_NAME for logical volumes, or the device name otherwise */
184         ddev = discover_device_create(udev->handler, uuid, devname ?: name);
185
186         if (devname) {
187                 /*
188                  * For logical volumes udev_device_get_devnode() returns a path
189                  * of the form "/dev/dm-xx". These nodes names are not
190                  * persistent and are opaque to the user. Instead use the more
191                  * recognisable "/dev/mapper/lv-name" node if it is available.
192                  */
193                 devlinks = talloc_strdup(ddev,
194                                 udev_device_get_property_value(dev, "DEVLINKS"));
195                 link = devlinks ? strtok_r(devlinks, " ", &saveptr) : NULL;
196                 while (link) {
197                         if (strncmp(link, "/dev/mapper/",
198                                         strlen("/dev/mapper/")) == 0) {
199                                 node = link;
200                                 break;
201                         }
202                         link = strtok_r(NULL, " ", &saveptr);
203                 }
204         }
205
206         ddev->device_path = talloc_strdup(ddev, node);
207         talloc_free(devlinks);
208
209         if (uuid)
210                 ddev->uuid = talloc_strdup(ddev, uuid);
211         prop = udev_device_get_property_value(dev, "ID_FS_LABEL");
212         if (prop)
213                 ddev->label = talloc_strdup(ddev, prop);
214
215         usb = !!udev_device_get_property_value(dev, "ID_USB_DRIVER");
216         if (cdrom)
217                 ddev->device->type = DEVICE_TYPE_OPTICAL;
218         else
219                 ddev->device->type = usb ? DEVICE_TYPE_USB : DEVICE_TYPE_DISK;
220
221         udev_setup_device_params(dev, ddev);
222
223         /* Create a snapshot for all disk devices */
224         if ((ddev->device->type == DEVICE_TYPE_DISK ||
225              ddev->device->type == DEVICE_TYPE_USB))
226                 devmapper_init_snapshot(udev->handler, ddev);
227
228         device_handler_discover(udev->handler, ddev);
229
230         return 0;
231 }
232
233 static int udev_handle_dev_add(struct pb_udev *udev, struct udev_device *dev)
234 {
235         const char *subsys;
236         const char *name;
237
238         name = udev_device_get_sysname(dev);
239         if (!name) {
240                 pb_debug("udev_device_get_sysname failed\n");
241                 return -1;
242         }
243
244         subsys = udev_device_get_subsystem(dev);
245         if (!subsys) {
246                 pb_debug("udev_device_get_subsystem failed\n");
247                 return -1;
248         }
249
250         if (device_lookup_by_id(udev->handler, name)) {
251                 pb_debug("device %s is already present?\n", name);
252                 return -1;
253         }
254
255         if (!strcmp(subsys, "block")) {
256                 return udev_handle_block_add(udev, dev, name);
257         }
258
259         pb_log("SKIP %s: unknown subsystem %s\n", name, subsys);
260         return -1;
261 }
262
263
264 static int udev_handle_dev_remove(struct pb_udev *udev, struct udev_device *dev)
265 {
266         struct discover_device *ddev;
267         const char *name;
268
269         name = udev_device_get_sysname(dev);
270         if (!name) {
271                 pb_debug("udev_device_get_sysname failed\n");
272                 return -1;
273         }
274
275         ddev = device_lookup_by_id(udev->handler, name);
276         if (!ddev)
277                 return 0;
278
279         device_handler_remove(udev->handler, ddev);
280
281         return 0;
282 }
283
284 /* returns true if further event processing should stop (eg., we've
285  * ejected the cdrom)
286  */
287 static bool udev_handle_cdrom_events(struct pb_udev *udev,
288                 struct udev_device *dev, struct discover_device *ddev)
289 {
290         const char *node;
291         bool eject = false;
292
293         node = udev_device_get_devnode(dev);
294
295         /* handle CDROM eject requests */
296         if (udev_device_get_property_value(dev, "DISK_EJECT_REQUEST")) {
297                 pb_debug("udev: eject request\n");
298
299                 /* If the device is mounted, cdrom_id's own eject request may
300                  * have failed. So, we'll need to do our own here.
301                  */
302                 if (ddev) {
303                         eject = ddev->mounted;
304                         udev_handle_dev_remove(udev, dev);
305                 }
306
307                 if (eject)
308                         cdrom_eject(node);
309
310                 return true;
311         }
312
313         if (udev_device_get_property_value(dev, "DISK_MEDIA_CHANGE")) {
314                 if (cdrom_media_present(node))
315                         udev_handle_dev_add(udev, dev);
316                 else
317                         udev_handle_dev_remove(udev, dev);
318                 return true;
319         }
320
321         return false;
322 }
323
324 static int udev_handle_dev_change(struct pb_udev *udev, struct udev_device *dev)
325 {
326         struct discover_device *ddev;
327         const char *name;
328         int rc = 0;
329
330         name = udev_device_get_sysname(dev);
331
332         ddev = device_lookup_by_id(udev->handler, name);
333
334         /* if this is a CDROM device, process eject & media change requests;
335          * these may stop further processing */
336         if (!udev_device_get_property_value(dev, "ID_CDROM")) {
337                 if (udev_handle_cdrom_events(udev, dev, ddev))
338                         return 0;
339         }
340
341         /* if this is a new device, treat it as an add */
342         if (!ddev)
343                 rc = udev_handle_dev_add(udev, dev);
344
345         return rc;
346 }
347
348 static int udev_handle_dev_action(struct udev_device *dev, const char *action)
349 {
350         struct pb_udev *udev = udev_get_userdata(udev_device_get_udev(dev));
351         struct udev_list_entry *list;
352         const char *name;
353
354         list = udev_device_get_properties_list_entry(dev);
355         name = udev_device_get_sysname(dev);
356
357         pb_debug("udev: action %s, device %s\n", action, name);
358         pb_debug("udev: properties:\n");
359
360         for (; list; list = udev_list_entry_get_next(list))
361                 pb_debug("\t%-20s: %s\n", udev_list_entry_get_name(list),
362                                 udev_list_entry_get_value(list));
363
364         if (!strcmp(action, "add"))
365                 return udev_handle_dev_add(udev, dev);
366
367         else if (!strcmp(action, "remove"))
368                 return udev_handle_dev_remove(udev, dev);
369
370         else if (!strcmp(action, "change"))
371                 return udev_handle_dev_change(udev, dev);
372
373         return 0;
374 }
375
376 static int udev_enumerate(struct udev *udev)
377 {
378         int result;
379         struct udev_list_entry *list, *entry;
380         struct udev_enumerate *enumerate;
381
382         enumerate = udev_enumerate_new(udev);
383
384         if (!enumerate) {
385                 pb_log("udev_enumerate_new failed\n");
386                 return -1;
387         }
388
389         result = udev_enumerate_add_match_subsystem(enumerate, "block");
390         if (result) {
391                 pb_log("udev_enumerate_add_match_subsystem failed\n");
392                 goto fail;
393         }
394
395         result = udev_enumerate_add_match_is_initialized(enumerate);
396         if (result) {
397                 pb_log("udev_enumerate_add_match_is_initialised failed\n");
398                 goto fail;
399         }
400
401         udev_enumerate_scan_devices(enumerate);
402
403         list = udev_enumerate_get_list_entry(enumerate);
404
405         udev_list_entry_foreach(entry, list) {
406                 const char *syspath;
407                 struct udev_device *dev;
408
409                 syspath = udev_list_entry_get_name(entry);
410                 dev = udev_device_new_from_syspath(udev, syspath);
411
412                 udev_handle_dev_action(dev, "add");
413
414                 udev_device_unref(dev);
415         }
416
417         udev_enumerate_unref(enumerate);
418         return 0;
419
420 fail:
421         udev_enumerate_unref(enumerate);
422         return -1;
423 }
424
425 static int udev_setup_monitor(struct udev *udev, struct udev_monitor **monitor)
426 {
427         int result;
428         struct udev_monitor *m;
429
430         *monitor = NULL;
431         m = udev_monitor_new_from_netlink(udev, "udev");
432
433         if (!m) {
434                 pb_log("udev_monitor_new_from_netlink failed\n");
435                 goto out_err;
436         }
437
438         result = udev_monitor_set_receive_buffer_size(m, monitor_bufsize);
439         if (result) {
440                 pb_log("udev_monitor_set_rx_bufsize(%d) failed\n",
441                         monitor_bufsize);
442         }
443
444         result = udev_monitor_filter_add_match_subsystem_devtype(m, "block",
445                 NULL);
446
447         if (result) {
448                 pb_log("udev_monitor_filter_add_match_subsystem_devtype failed\n");
449                 goto out_err;
450         }
451
452         result = udev_monitor_enable_receiving(m);
453
454         if (result) {
455                 pb_log("udev_monitor_enable_receiving failed\n");
456                 goto out_err;
457         }
458
459         *monitor = m;
460         return 0;
461
462 out_err:
463         udev_monitor_unref(m);
464         return -1;
465 }
466
467 /*
468  * udev_process - waiter callback for monitor netlink.
469  */
470
471 static int udev_process(void *arg)
472 {
473         struct udev_monitor *monitor = arg;
474         struct udev_device *dev;
475         const char *action;
476
477         dev = udev_monitor_receive_device(monitor);
478         if (!dev) {
479                 pb_log("udev_monitor_receive_device failed\n");
480                 return -1;
481         }
482
483         action = udev_device_get_action(dev);
484
485         if (!action) {
486                 pb_log("udev_device_get_action failed\n");
487         } else {
488                 udev_handle_dev_action(dev, action);
489         }
490
491         udev_device_unref(dev);
492         return 0;
493 }
494
495 #ifdef UDEV_LOGGING
496 static void udev_log_fn(struct udev __attribute__((unused)) *udev,
497         int __attribute__((unused)) priority, const char *file, int line,
498         const char *fn, const char *format, va_list args)
499 {
500       pb_log("libudev: %s %s:%d: ", fn, file, line);
501       vfprintf(pb_log_get_stream(), format, args);
502 }
503 #endif
504
505 struct pb_udev *udev_init(struct device_handler *handler,
506                 struct waitset *waitset)
507 {
508         struct pb_udev *udev;
509         int result;
510
511         udev = talloc_zero(handler, struct pb_udev);
512         talloc_set_destructor(udev, udev_destructor);
513         udev->handler = handler;
514
515         udev->udev = udev_new();
516
517         if (!udev->udev) {
518                 pb_log("udev_new failed\n");
519                 goto fail;
520         }
521
522         udev_set_userdata(udev->udev, udev);
523
524 #ifdef UDEV_LOGGING
525         udev_set_log_fn(udev->udev, udev_log_fn);
526 #endif
527
528         result = udev_setup_monitor(udev->udev, &udev->monitor);
529         if (result)
530                 goto fail;
531
532         result = udev_enumerate(udev->udev);
533         if (result)
534                 goto fail;
535
536         waiter_register_io(waitset, udev_monitor_get_fd(udev->monitor), WAIT_IN,
537                 udev_process, udev->monitor);
538
539         pb_debug("%s: waiting on udev\n", __func__);
540
541         return udev;
542
543 fail:
544         talloc_free(udev);
545         return NULL;
546 }
547
548 void udev_reinit(struct pb_udev *udev)
549 {
550         pb_log("udev: reinit requested, starting enumeration\n");
551         udev_enumerate(udev->udev);
552 }