discover: log unresolved boot options
[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 <talloc/talloc.h>
17 #include <waiter/waiter.h>
18 #include <system/system.h>
19
20 #include "event.h"
21 #include "udev.h"
22 #include "pb-discover.h"
23 #include "device-handler.h"
24
25 #if defined(DEBUG)
26 #define DBG(fmt, args...) pb_log("DBG: " fmt, ## args)
27 #define DBGS(fmt, args...) \
28         pb_log("DBG:%s:%d: " fmt, __func__, __LINE__, ## args)
29 #else
30 #define DBG(fmt, args...)
31 #define DBGS(fmt, args...)
32 #endif
33
34 struct pb_udev {
35         struct udev *udev;
36         struct udev_monitor *monitor;
37         struct device_handler *handler;
38 };
39
40 static int udev_destructor(void *p)
41 {
42         struct pb_udev *udev = p;
43
44         udev_monitor_unref(udev->monitor);
45         udev->monitor = NULL;
46
47         udev_unref(udev->udev);
48         udev->udev = NULL;
49
50         return 0;
51 }
52
53 static void print_device_properties(struct udev_device *dev)
54 {
55         struct udev_list_entry *list, *entry;
56
57         assert(dev);
58
59         if (1) {
60                 list = udev_device_get_properties_list_entry(dev);
61
62                 assert(list);
63
64                 udev_list_entry_foreach(entry, list)
65                         DBG("property: %s - %s\n",
66                                 udev_list_entry_get_name(entry),
67                                 udev_device_get_property_value(dev,
68                                         udev_list_entry_get_name(entry)));
69         }
70 }
71
72 static int udev_handle_dev_action(struct udev_device *dev, const char *action)
73 {
74         const char *devtype;
75         const char *devpath;
76         const char *devnode;
77         struct pb_udev *udev;
78         struct event *event;
79         enum event_action eva = 0;
80
81         assert(dev);
82         assert(action);
83
84         devtype = udev_device_get_devtype(dev); /* DEVTYPE */
85
86         if (!devtype) {
87                 pb_log("udev_device_get_devtype failed\n");
88                 return -1;
89         }
90
91         devpath = udev_device_get_devpath(dev); /* DEVPATH */
92
93         if (!devpath) {
94                 pb_log("udev_device_get_devpath failed\n");
95                 return -1;
96         }
97
98         devnode = udev_device_get_devnode(dev); /* DEVNAME */
99
100         if (!devnode) {
101                 pb_log("udev_device_get_devnode failed\n");
102                 return -1;
103         }
104
105         print_device_properties(dev);
106
107         /* Ignore non disk or partition, ram, loop. */
108
109         if (!(strstr(devtype, "disk") || strstr(devtype, "partition"))
110                 || strstr(devpath, "virtual/block/loop")
111                 || strstr(devpath, "virtual/block/ram")) {
112                 pb_log("SKIP: %s - %s\n", devtype, devnode);
113                 return 0;
114         }
115
116         if (!strcmp(action, "add")) {
117                 pb_log("ADD: %s - %s\n", devtype, devnode);
118                 eva = EVENT_ACTION_ADD;
119         } else if (!strcmp(action, "remove")) {
120                 pb_log("REMOVE: %s - %s\n", devtype, devnode);
121                 eva = EVENT_ACTION_REMOVE;
122         } else {
123                 pb_log("SKIP: %s: %s - %s\n", action, devtype, devnode);
124                 return 0;
125         }
126
127         event = talloc(NULL, struct event);
128
129         event->type = EVENT_TYPE_UDEV;
130         event->action = eva;
131         event->device = devpath;
132
133         event->n_params = 1;
134         event->params = talloc(event, struct param);
135         event->params->name = "DEVNAME";
136         event->params->value = devnode;
137
138         udev = udev_get_userdata(udev_device_get_udev(dev));
139         assert(udev);
140
141         device_handler_event(udev->handler, event);
142
143         talloc_free(event);
144         return 0;
145 }
146
147 static int udev_enumerate(struct udev *udev)
148 {
149         int result;
150         struct udev_list_entry *list, *entry;
151         struct udev_enumerate *enumerate;
152
153         enumerate = udev_enumerate_new(udev);
154
155         if (!enumerate) {
156                 pb_log("udev_enumerate_new failed\n");
157                 return -1;
158         }
159
160         result = udev_enumerate_add_match_subsystem(enumerate, "block");
161
162         if (result) {
163                 pb_log("udev_enumerate_add_match_subsystem failed\n");
164                 goto fail;
165         }
166
167         udev_enumerate_scan_devices(enumerate);
168
169         list = udev_enumerate_get_list_entry(enumerate);
170
171         if (!list) {
172                 pb_log("udev_enumerate_get_list_entry failed\n");
173                 goto fail;
174         }
175
176         udev_list_entry_foreach(entry, list) {
177                 const char *syspath;
178                 struct udev_device *dev;
179
180                 syspath = udev_list_entry_get_name(entry);
181                 dev = udev_device_new_from_syspath(udev, syspath);
182
183                 udev_handle_dev_action(dev, "add");
184
185                 udev_device_unref(dev);
186         }
187
188         udev_enumerate_unref(enumerate);
189         return 0;
190
191 fail:
192         udev_enumerate_unref(enumerate);
193         return -1;
194 }
195
196 static int udev_setup_monitor(struct udev *udev, struct udev_monitor **monitor)
197 {
198         int result;
199         struct udev_monitor *m;
200
201         *monitor = NULL;
202         m = udev_monitor_new_from_netlink(udev, "udev");
203
204         if (!m) {
205                 pb_log("udev_monitor_new_from_netlink failed\n");
206                 goto out_err;
207         }
208
209         result = udev_monitor_filter_add_match_subsystem_devtype(m, "block",
210                 NULL);
211
212         if (result) {
213                 pb_log("udev_monitor_filter_add_match_subsystem_devtype failed\n");
214                 goto out_err;
215         }
216
217         result = udev_monitor_enable_receiving(m);
218
219         if (result) {
220                 pb_log("udev_monitor_enable_receiving failed\n");
221                 goto out_err;
222         }
223
224         *monitor = m;
225         return 0;
226
227 out_err:
228         udev_monitor_unref(m);
229         return -1;
230 }
231
232 /*
233  * udev_process - waiter callback for monitor netlink.
234  */
235
236 static int udev_process(void *arg)
237 {
238         struct udev_monitor *monitor = arg;
239         struct udev_device *dev;
240         const char *action;
241         int result;
242
243         dev = udev_monitor_receive_device(monitor);
244
245         if (!dev) {
246                 pb_log("udev_monitor_receive_device failed\n");
247                 return -1;
248         }
249
250         action = udev_device_get_action(dev);
251
252         if (!action) {
253                 pb_log("udev_device_get_action failed\n");
254                 goto fail;
255         }
256
257         result = udev_handle_dev_action(dev, action);
258
259         udev_device_unref(dev);
260         return result;
261
262 fail:
263         udev_device_unref(dev);
264         return -1;
265 }
266
267 static void udev_log_fn(struct udev __attribute__((unused)) *udev,
268         int __attribute__((unused)) priority, const char *file, int line,
269         const char *fn, const char *format, va_list args)
270 {
271       pb_log("libudev: %s %s:%d: ", fn, file, line);
272       vfprintf(pb_log_get_stream(), format, args);
273 }
274
275 struct pb_udev *udev_init(struct waitset *waitset,
276         struct device_handler *handler)
277 {
278         int result;
279         struct pb_udev *udev = talloc(NULL, struct pb_udev);
280
281         talloc_set_destructor(udev, udev_destructor);
282         udev->handler = handler;
283
284         udev->udev = udev_new();
285
286         if (!udev->udev) {
287                 pb_log("udev_new failed\n");
288                 goto fail_new;
289         }
290
291         udev_set_userdata(udev->udev, udev);
292
293         udev_set_log_fn(udev->udev, udev_log_fn);
294
295         result = udev_enumerate(udev->udev);
296
297         if (result)
298                 goto fail_enumerate;
299
300         result = udev_setup_monitor(udev->udev, &udev->monitor);
301
302         if (result)
303                 goto fail_monitor;
304
305         waiter_register(waitset, udev_monitor_get_fd(udev->monitor), WAIT_IN,
306                 udev_process, udev->monitor);
307
308         pb_log("%s: waiting on udev\n", __func__);
309
310         return udev;
311
312 fail_monitor:
313 fail_enumerate:
314         udev_unref(udev->udev);
315 fail_new:
316         talloc_free(udev);
317         return NULL;
318 }
319
320 void udev_destroy(struct pb_udev *udev)
321 {
322         talloc_free(udev);
323 }