]> git.ozlabs.org Git - petitboot/blob - discover/udev.c
d13ced7a22d175cf9684511d41e7ba52289d6020
[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         event_set_param(event, "type", devtype);
130
131         udev_setup_event_params(dev, event);
132
133         udev = udev_get_userdata(udev_device_get_udev(dev));
134         assert(udev);
135
136         device_handler_event(udev->handler, event);
137
138         talloc_free(event);
139         return 0;
140 }
141
142 static int udev_enumerate(struct udev *udev)
143 {
144         int result;
145         struct udev_list_entry *list, *entry;
146         struct udev_enumerate *enumerate;
147
148         enumerate = udev_enumerate_new(udev);
149
150         if (!enumerate) {
151                 pb_log("udev_enumerate_new failed\n");
152                 return -1;
153         }
154
155         result = udev_enumerate_add_match_subsystem(enumerate, "block");
156
157         if (result) {
158                 pb_log("udev_enumerate_add_match_subsystem failed\n");
159                 goto fail;
160         }
161
162         udev_enumerate_scan_devices(enumerate);
163
164         list = udev_enumerate_get_list_entry(enumerate);
165
166         if (!list) {
167                 pb_log("udev_enumerate_get_list_entry failed\n");
168                 goto fail;
169         }
170
171         udev_list_entry_foreach(entry, list) {
172                 const char *syspath;
173                 struct udev_device *dev;
174
175                 syspath = udev_list_entry_get_name(entry);
176                 dev = udev_device_new_from_syspath(udev, syspath);
177
178                 udev_handle_dev_action(dev, "add");
179
180                 udev_device_unref(dev);
181         }
182
183         udev_enumerate_unref(enumerate);
184         return 0;
185
186 fail:
187         udev_enumerate_unref(enumerate);
188         return -1;
189 }
190
191 static int udev_setup_monitor(struct udev *udev, struct udev_monitor **monitor)
192 {
193         int result;
194         struct udev_monitor *m;
195
196         *monitor = NULL;
197         m = udev_monitor_new_from_netlink(udev, "udev");
198
199         if (!m) {
200                 pb_log("udev_monitor_new_from_netlink failed\n");
201                 goto out_err;
202         }
203
204         result = udev_monitor_filter_add_match_subsystem_devtype(m, "block",
205                 NULL);
206
207         if (result) {
208                 pb_log("udev_monitor_filter_add_match_subsystem_devtype failed\n");
209                 goto out_err;
210         }
211
212         result = udev_monitor_enable_receiving(m);
213
214         if (result) {
215                 pb_log("udev_monitor_enable_receiving failed\n");
216                 goto out_err;
217         }
218
219         *monitor = m;
220         return 0;
221
222 out_err:
223         udev_monitor_unref(m);
224         return -1;
225 }
226
227 /*
228  * udev_process - waiter callback for monitor netlink.
229  */
230
231 static int udev_process(void *arg)
232 {
233         struct udev_monitor *monitor = arg;
234         struct udev_device *dev;
235         const char *action;
236         int result;
237
238         dev = udev_monitor_receive_device(monitor);
239
240         if (!dev) {
241                 pb_log("udev_monitor_receive_device failed\n");
242                 return -1;
243         }
244
245         action = udev_device_get_action(dev);
246
247         if (!action) {
248                 pb_log("udev_device_get_action failed\n");
249                 goto fail;
250         }
251
252         result = udev_handle_dev_action(dev, action);
253
254         udev_device_unref(dev);
255         return result;
256
257 fail:
258         udev_device_unref(dev);
259         return -1;
260 }
261
262 static void udev_log_fn(struct udev __attribute__((unused)) *udev,
263         int __attribute__((unused)) priority, const char *file, int line,
264         const char *fn, const char *format, va_list args)
265 {
266       pb_log("libudev: %s %s:%d: ", fn, file, line);
267       vfprintf(pb_log_get_stream(), format, args);
268 }
269
270 struct pb_udev *udev_init(struct waitset *waitset,
271         struct device_handler *handler)
272 {
273         int result;
274         struct pb_udev *udev = talloc(NULL, struct pb_udev);
275
276         talloc_set_destructor(udev, udev_destructor);
277         udev->handler = handler;
278
279         udev->udev = udev_new();
280
281         if (!udev->udev) {
282                 pb_log("udev_new failed\n");
283                 goto fail_new;
284         }
285
286         udev_set_userdata(udev->udev, udev);
287
288         udev_set_log_fn(udev->udev, udev_log_fn);
289
290         result = udev_enumerate(udev->udev);
291
292         if (result)
293                 goto fail_enumerate;
294
295         result = udev_setup_monitor(udev->udev, &udev->monitor);
296
297         if (result)
298                 goto fail_monitor;
299
300         waiter_register_io(waitset, udev_monitor_get_fd(udev->monitor), WAIT_IN,
301                 udev_process, udev->monitor);
302
303         pb_log("%s: waiting on udev\n", __func__);
304
305         return udev;
306
307 fail_monitor:
308 fail_enumerate:
309         udev_unref(udev->udev);
310 fail_new:
311         talloc_free(udev);
312         return NULL;
313 }
314
315 void udev_destroy(struct pb_udev *udev)
316 {
317         talloc_free(udev);
318 }