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