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