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