]> git.ozlabs.org Git - petitboot/blob - discover/udev.c
discover: Check for devices with duplicate serial properties
[petitboot] / discover / udev.c
1
2 #define _GNU_SOURCE
3
4 #include <assert.h>
5 #include <errno.h>
6 #include <libudev.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <sys/socket.h>
12 #include <sys/types.h>
13 #include <sys/un.h>
14
15 #include <log/log.h>
16 #include <types/types.h>
17 #include <talloc/talloc.h>
18 #include <waiter/waiter.h>
19 #include <system/system.h>
20
21 #include "event.h"
22 #include "udev.h"
23 #include "pb-discover.h"
24 #include "device-handler.h"
25
26 #if defined(DEBUG)
27 #define DBG(fmt, args...) pb_log("DBG: " fmt, ## args)
28 #define DBGS(fmt, args...) \
29         pb_log("DBG:%s:%d: " fmt, __func__, __LINE__, ## args)
30 #else
31 #define DBG(fmt, args...)
32 #define DBGS(fmt, args...)
33 #endif
34
35 struct pb_udev {
36         struct udev *udev;
37         struct udev_monitor *monitor;
38         struct device_handler *handler;
39 };
40
41 static int udev_destructor(void *p)
42 {
43         struct pb_udev *udev = p;
44
45         udev_monitor_unref(udev->monitor);
46         udev->monitor = NULL;
47
48         udev_unref(udev->udev);
49         udev->udev = NULL;
50
51         return 0;
52 }
53
54 static void udev_setup_device_params(struct udev_device *udev,
55                 struct discover_device *dev)
56 {
57         struct udev_list_entry *list, *entry;
58
59         list = udev_device_get_properties_list_entry(udev);
60         if (!list)
61                 return;
62
63         udev_list_entry_foreach(entry, list)
64                 discover_device_set_param(dev,
65                                 udev_list_entry_get_name(entry),
66                                 udev_list_entry_get_value(entry));
67 }
68
69 static int udev_handle_dev_add(struct pb_udev *udev, struct udev_device *dev)
70 {
71         struct discover_device *ddev;
72         const char *typestr;
73         const char *serial;
74         const char *path;
75         const char *name;
76
77         name = udev_device_get_sysname(dev);
78         if (!name) {
79                 pb_debug("udev_device_get_sysname failed\n");
80                 return -1;
81         }
82
83         typestr = udev_device_get_devtype(dev);
84         if (!typestr) {
85                 pb_debug("udev_device_get_devtype failed\n");
86                 return -1;
87         }
88
89         if (!(!strcmp(typestr, "disk") || !strcmp(typestr, "partition"))) {
90                 pb_debug("SKIP %s: invalid type %s\n", name, typestr);
91                 return 0;
92         }
93
94         path = udev_device_get_devpath(dev);
95         if (path && (strstr(path, "virtual/block/loop")
96                         || strstr(path, "virtual/block/ram"))) {
97                 pb_debug("SKIP: %s: ignored (path=%s)\n", name, path);
98                 return 0;
99         }
100
101         /* We have enough info to create the device and start discovery */
102         ddev = device_lookup_by_id(udev->handler, name);
103         if (ddev) {
104                 pb_debug("device %s is already present?\n", name);
105                 return -1;
106         }
107
108         /* we may also see multipath devices; same dev nodes (hence id), but
109          * different serial numbers */
110         serial = udev_device_get_property_value(dev, "ID_SERIAL");
111         if (serial && device_lookup_by_serial(udev->handler, serial))
112                 return -1;
113
114         ddev = discover_device_create(udev->handler, name);
115
116         ddev->device_path = udev_device_get_devnode(dev);
117         ddev->uuid = udev_device_get_property_value(dev, "ID_FS_UUID");
118         ddev->label = udev_device_get_property_value(dev, "ID_FS_LABEL");
119         ddev->device->type = DEVICE_TYPE_DISK;
120
121         udev_setup_device_params(dev, ddev);
122
123         device_handler_discover(udev->handler, ddev, CONF_METHOD_LOCAL_FILE);
124
125         return 0;
126 }
127
128 static int udev_handle_dev_remove(struct pb_udev *udev, struct udev_device *dev)
129 {
130         struct discover_device *ddev;
131         const char *name;
132
133         name = udev_device_get_sysname(dev);
134         if (!name) {
135                 pb_debug("udev_device_get_sysname failed\n");
136                 return -1;
137         }
138
139         ddev = device_lookup_by_id(udev->handler, name);
140         if (!ddev)
141                 return 0;
142
143         device_handler_remove(udev->handler, ddev);
144
145         return 0;
146 }
147 static int udev_handle_dev_action(struct udev_device *dev, const char *action)
148 {
149         struct pb_udev *udev = udev_get_userdata(udev_device_get_udev(dev));
150
151         if (!strcmp(action, "add"))
152                 return udev_handle_dev_add(udev, dev);
153
154         else if (!strcmp(action, "remove"))
155                 return udev_handle_dev_remove(udev, dev);
156
157         return 0;
158 }
159
160 static int udev_enumerate(struct udev *udev)
161 {
162         int result;
163         struct udev_list_entry *list, *entry;
164         struct udev_enumerate *enumerate;
165
166         enumerate = udev_enumerate_new(udev);
167
168         if (!enumerate) {
169                 pb_log("udev_enumerate_new failed\n");
170                 return -1;
171         }
172
173         result = udev_enumerate_add_match_subsystem(enumerate, "block");
174
175         if (result) {
176                 pb_log("udev_enumerate_add_match_subsystem failed\n");
177                 goto fail;
178         }
179
180         udev_enumerate_scan_devices(enumerate);
181
182         list = udev_enumerate_get_list_entry(enumerate);
183
184         if (!list) {
185                 pb_log("udev_enumerate_get_list_entry failed\n");
186                 goto fail;
187         }
188
189         udev_list_entry_foreach(entry, list) {
190                 const char *syspath;
191                 struct udev_device *dev;
192
193                 syspath = udev_list_entry_get_name(entry);
194                 dev = udev_device_new_from_syspath(udev, syspath);
195
196                 udev_handle_dev_action(dev, "add");
197
198                 udev_device_unref(dev);
199         }
200
201         udev_enumerate_unref(enumerate);
202         return 0;
203
204 fail:
205         udev_enumerate_unref(enumerate);
206         return -1;
207 }
208
209 static int udev_setup_monitor(struct udev *udev, struct udev_monitor **monitor)
210 {
211         int result;
212         struct udev_monitor *m;
213
214         *monitor = NULL;
215         m = udev_monitor_new_from_netlink(udev, "udev");
216
217         if (!m) {
218                 pb_log("udev_monitor_new_from_netlink failed\n");
219                 goto out_err;
220         }
221
222         result = udev_monitor_filter_add_match_subsystem_devtype(m, "block",
223                 NULL);
224
225         if (result) {
226                 pb_log("udev_monitor_filter_add_match_subsystem_devtype failed\n");
227                 goto out_err;
228         }
229
230         result = udev_monitor_enable_receiving(m);
231
232         if (result) {
233                 pb_log("udev_monitor_enable_receiving failed\n");
234                 goto out_err;
235         }
236
237         *monitor = m;
238         return 0;
239
240 out_err:
241         udev_monitor_unref(m);
242         return -1;
243 }
244
245 /*
246  * udev_process - waiter callback for monitor netlink.
247  */
248
249 static int udev_process(void *arg)
250 {
251         struct udev_monitor *monitor = arg;
252         struct udev_device *dev;
253         const char *action;
254         int result;
255
256         dev = udev_monitor_receive_device(monitor);
257
258         if (!dev) {
259                 pb_log("udev_monitor_receive_device failed\n");
260                 return -1;
261         }
262
263         action = udev_device_get_action(dev);
264
265         if (!action) {
266                 pb_log("udev_device_get_action failed\n");
267                 goto fail;
268         }
269
270         result = udev_handle_dev_action(dev, action);
271
272         udev_device_unref(dev);
273         return result;
274
275 fail:
276         udev_device_unref(dev);
277         return -1;
278 }
279
280 static void udev_log_fn(struct udev __attribute__((unused)) *udev,
281         int __attribute__((unused)) priority, const char *file, int line,
282         const char *fn, const char *format, va_list args)
283 {
284       pb_log("libudev: %s %s:%d: ", fn, file, line);
285       vfprintf(pb_log_get_stream(), format, args);
286 }
287
288 struct pb_udev *udev_init(struct waitset *waitset,
289         struct device_handler *handler)
290 {
291         int result;
292         struct pb_udev *udev = talloc(NULL, struct pb_udev);
293
294         talloc_set_destructor(udev, udev_destructor);
295         udev->handler = handler;
296
297         udev->udev = udev_new();
298
299         if (!udev->udev) {
300                 pb_log("udev_new failed\n");
301                 goto fail_new;
302         }
303
304         udev_set_userdata(udev->udev, udev);
305
306         udev_set_log_fn(udev->udev, udev_log_fn);
307
308         result = udev_enumerate(udev->udev);
309
310         if (result)
311                 goto fail_enumerate;
312
313         result = udev_setup_monitor(udev->udev, &udev->monitor);
314
315         if (result)
316                 goto fail_monitor;
317
318         waiter_register_io(waitset, udev_monitor_get_fd(udev->monitor), WAIT_IN,
319                 udev_process, udev->monitor);
320
321         pb_log("%s: waiting on udev\n", __func__);
322
323         return udev;
324
325 fail_monitor:
326 fail_enumerate:
327         udev_unref(udev->udev);
328 fail_new:
329         talloc_free(udev);
330         return NULL;
331 }
332
333 void udev_destroy(struct pb_udev *udev)
334 {
335         talloc_free(udev);
336 }