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