6747e78a8064f7a04a5b377b3535a6a412658a71
[petitboot] / udev.c
1
2 #define _GNU_SOURCE
3
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/un.h>
11
12 #include <talloc/talloc.h>
13
14 #include "udev.h"
15 #include "log.h"
16 #include "waiter.h"
17 #include "pb-discover.h"
18 #include "device-handler.h"
19
20 #define PBOOT_DEVICE_SOCKET "/tmp/petitboot.udev"
21
22 #define max(a, b) ((a) > (b) ? (a) : (b))
23
24 struct udev {
25         struct device_handler *handler;
26         int socket;
27 };
28
29 static void parse_event_params(struct udev_event *event, char *buf, int len)
30 {
31         int param_len, name_len, value_len;
32         struct param *param;
33         char *sep;
34
35         for (; len > 0; len -= param_len + 1, buf += param_len + 1) {
36
37                 /* find the length of the whole parameter */
38                 param_len = strnlen(buf, len);
39                 if (!param_len) {
40                         /* multiple NULs? skip over */
41                         param_len = 1;
42                         continue;
43                 }
44
45                 /* find the separator */
46                 sep = memchr(buf, '=', param_len);
47                 if (!sep)
48                         continue;
49
50                 name_len = sep - buf;
51                 value_len = param_len - name_len - 1;
52
53                 /* update the params array */
54                 event->params = talloc_realloc(event, event->params,
55                                         struct param, ++event->n_params);
56                 param = &event->params[event->n_params - 1];
57
58                 param->name = talloc_strndup(event, buf, name_len);
59                 param->value = talloc_strndup(event, sep + 1, value_len);
60         }
61 }
62
63 const char *udev_event_param(struct udev_event *event, const char *name)
64 {
65         int i;
66
67         for (i = 0; i < event->n_params; i++)
68                 if (!strcasecmp(event->params[i].name, name))
69                         return event->params[i].value;
70
71         return NULL;
72 }
73
74 static void print_event(struct udev_event *event)
75 {
76         const char *action, *params[] = {
77                 "DEVNAME", "ID_TYPE", "ID_BUS", "ID_FS_UUID", "ID_FS_LABEL",
78                 NULL,
79         };
80         int i;
81
82         action = event->action == UDEV_ACTION_ADD ? "add" : "remove";
83
84         pb_log("udev %s event:\n", action);
85         printf("\tdevice: %s\n", event->device);
86
87         for (i = 0; params[i]; i++)
88                 printf("\t%-12s => %s\n",
89                                 params[i], udev_event_param(event, params[i]));
90
91 }
92
93 static void handle_udev_message(struct udev *udev, char *buf, int len)
94 {
95         char *sep, *device;
96         enum udev_action action;
97         struct udev_event *event;
98         int device_len;
99
100         /* we should see an <action>@<device>\0 at the head of the buffer */
101         sep = strchr(buf, '@');
102         if (!sep)
103                 return;
104
105         /* terminate the action string */
106         *sep = '\0';
107         len -= sep - buf + 1;
108
109         if (!strcmp(buf, "add")) {
110                 action = UDEV_ACTION_ADD;
111
112         } else if (!strcmp(buf, "remove")) {
113                 action = UDEV_ACTION_REMOVE;
114
115         } else {
116                 return;
117         }
118
119         /* initialise the device string */
120         device = sep + 1;
121         device_len = strnlen(device, len);
122         if (!device_len)
123                 return;
124
125         /* now we have an action and a device, we can construct an event */
126         event = talloc(udev, struct udev_event);
127         event->action = action;
128         event->device = talloc_strndup(event, device, device_len);
129         event->n_params = 0;
130         event->params = NULL;
131
132         len -= device_len + 1;
133         parse_event_params(event, device + device_len + 1, len);
134
135         print_event(event);
136
137         device_handler_event(udev->handler, event);
138
139         talloc_free(event);
140
141         return;
142 }
143
144 static int udev_process(void *arg)
145 {
146         struct udev *udev = arg;
147         char buf[4096];
148         int len;
149
150         len = recvfrom(udev->socket, buf, sizeof(buf), 0, NULL, NULL);
151
152         if (len < 0) {
153                 pb_log("udev socket read failed: %s", strerror(errno));
154                 return -1;
155         }
156
157         if (len == 0)
158                 return 0;
159
160         handle_udev_message(udev, buf, len);
161
162         return 0;
163 }
164
165 static int udev_destructor(void *p)
166 {
167         struct udev *udev = p;
168
169         if (udev->socket >= 0)
170                 close(udev->socket);
171
172         return 0;
173 }
174
175 struct udev *udev_init(struct device_handler *handler)
176 {
177         struct sockaddr_un addr;
178         struct udev *udev;
179
180         unlink(PBOOT_DEVICE_SOCKET);
181
182         udev = talloc(NULL, struct udev);
183
184         udev->handler = handler;
185
186         udev->socket = socket(PF_UNIX, SOCK_DGRAM, 0);
187         if (udev->socket < 0) {
188                 pb_log("Error creating udev socket: %s\n", strerror(errno));
189                 goto out_err;
190         }
191
192         talloc_set_destructor(udev, udev_destructor);
193
194         addr.sun_family = AF_UNIX;
195         strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET);
196
197         if (bind(udev->socket, (struct sockaddr *)&addr, sizeof(addr))) {
198                 pb_log("Error binding udev socket: %s\n", strerror(errno));
199                 goto out_err;
200         }
201
202         waiter_register(udev->socket, WAIT_IN, udev_process, udev);
203
204         return udev;
205
206 out_err:
207         talloc_free(udev);
208         return NULL;
209 }
210
211 void udev_destroy(struct udev *udev)
212 {
213         talloc_free(udev);
214 }