linux_netlink: Add useful debug messages and clean up error handling
[platform/upstream/libusb.git] / libusb / os / linux_netlink.c
1 /* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */
2 /*
3  * Linux usbfs backend for libusb
4  * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org>
5  * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
6  * Copyright (c) 2013 Nathan Hjelm <hjelmn@mac.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22
23 #include <config.h>
24
25 #include <assert.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <poll.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34
35 #ifdef HAVE_ASM_TYPES_H
36 #include <asm/types.h>
37 #endif
38
39 #include <sys/socket.h>
40 #include <linux/netlink.h>
41
42 #include "libusbi.h"
43 #include "linux_usbfs.h"
44
45 #define KERNEL 1
46
47 static int linux_netlink_socket = -1;
48 static int netlink_control_pipe[2] = { -1, -1 };
49 static pthread_t libusb_linux_event_thread;
50
51 static void *linux_netlink_event_thread_main(void *arg);
52
53 static struct sockaddr_nl snl = { .nl_family = AF_NETLINK, .nl_groups = KERNEL };
54
55 static int set_fd_cloexec_nb(int fd)
56 {
57         int flags;
58
59 #if defined(FD_CLOEXEC)
60         flags = fcntl(fd, F_GETFD);
61         if (flags == -1) {
62                 usbi_err(NULL, "failed to get netlink fd flags (%d)", errno);
63                 return -1;
64         }
65
66         if (!(flags & FD_CLOEXEC)) {
67                 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
68                         usbi_err(NULL, "failed to set netlink fd flags (%d)", errno);
69                         return -1;
70                 }
71         }
72 #endif
73
74         flags = fcntl(fd, F_GETFL);
75         if (flags == -1) {
76                 usbi_err(NULL, "failed to get netlink fd status flags (%d)", errno);
77                 return -1;
78         }
79
80         if (!(flags & O_NONBLOCK)) {
81                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
82                         usbi_err(NULL, "failed to set netlink fd status flags (%d)", errno);
83                         return -1;
84                 }
85         }
86
87         return 0;
88 }
89
90 int linux_netlink_start_event_monitor(void)
91 {
92         int socktype = SOCK_RAW;
93         int ret;
94
95 #if defined(SOCK_CLOEXEC)
96         socktype |= SOCK_CLOEXEC;
97 #endif
98 #if defined(SOCK_NONBLOCK)
99         socktype |= SOCK_NONBLOCK;
100 #endif
101
102         linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT);
103         if (linux_netlink_socket == -1 && errno == EINVAL) {
104                 usbi_dbg("failed to create netlink socket of type %d, attempting SOCK_RAW", socktype);
105                 linux_netlink_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
106         }
107
108         if (linux_netlink_socket == -1) {
109                 usbi_err(NULL, "failed to create netlink socket (%d)", errno);
110                 goto err;
111         }
112
113         ret = set_fd_cloexec_nb(linux_netlink_socket);
114         if (ret == -1)
115                 goto err_close_socket;
116
117         ret = bind(linux_netlink_socket, (struct sockaddr *)&snl, sizeof(snl));
118         if (ret == -1) {
119                 usbi_err(NULL, "failed to bind netlink socket (%d)", errno);
120                 goto err_close_socket;
121         }
122
123         /* TODO -- add authentication */
124         /* setsockopt(linux_netlink_socket, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); */
125
126         ret = usbi_pipe(netlink_control_pipe);
127         if (ret) {
128                 usbi_err(NULL, "failed to create netlink control pipe");
129                 goto err_close_socket;
130         }
131
132         ret = pthread_create(&libusb_linux_event_thread, NULL, linux_netlink_event_thread_main, NULL);
133         if (ret != 0) {
134                 usbi_err(NULL, "failed to create netlink event thread (%d)", ret);
135                 goto err_close_pipe;
136         }
137
138         return LIBUSB_SUCCESS;
139
140 err_close_pipe:
141         close(netlink_control_pipe[0]);
142         close(netlink_control_pipe[1]);
143         netlink_control_pipe[0] = -1;
144         netlink_control_pipe[1] = -1;
145 err_close_socket:
146         close(linux_netlink_socket);
147         linux_netlink_socket = -1;
148 err:
149         return LIBUSB_ERROR_OTHER;
150 }
151
152 int linux_netlink_stop_event_monitor(void)
153 {
154         char dummy = 1;
155         ssize_t r;
156
157         assert(linux_netlink_socket != -1);
158
159         /* Write some dummy data to the control pipe and
160          * wait for the thread to exit */
161         r = usbi_write(netlink_control_pipe[1], &dummy, sizeof(dummy));
162         if (r <= 0)
163                 usbi_warn(NULL, "netlink control pipe signal failed");
164
165         pthread_join(libusb_linux_event_thread, NULL);
166
167         close(linux_netlink_socket);
168         linux_netlink_socket = -1;
169
170         /* close and reset control pipe */
171         close(netlink_control_pipe[0]);
172         close(netlink_control_pipe[1]);
173         netlink_control_pipe[0] = -1;
174         netlink_control_pipe[1] = -1;
175
176         return LIBUSB_SUCCESS;
177 }
178
179 static const char *netlink_message_parse(const char *buffer, size_t len, const char *key)
180 {
181         size_t keylen = strlen(key);
182         size_t offset;
183
184         for (offset = 0; offset < len && buffer[offset] != '\0'; offset += strlen(buffer + offset) + 1) {
185                 if (strncmp(buffer + offset, key, keylen) == 0 &&
186                         buffer[offset + keylen] == '=') {
187                         return buffer + offset + keylen + 1;
188                 }
189         }
190
191         return NULL;
192 }
193
194 /* parse parts of netlink message common to both libudev and the kernel */
195 static int linux_netlink_parse(const char *buffer, size_t len, int *detached,
196         const char **sys_name, uint8_t *busnum, uint8_t *devaddr)
197 {
198         const char *tmp;
199         int i;
200
201         errno = 0;
202
203         *sys_name = NULL;
204         *detached = 0;
205         *busnum   = 0;
206         *devaddr  = 0;
207
208         tmp = netlink_message_parse(buffer, len, "ACTION");
209         if (!tmp) {
210                 return -1;
211         } else if (strcmp(tmp, "remove") == 0) {
212                 *detached = 1;
213         } else if (strcmp(tmp, "add") != 0) {
214                 usbi_dbg("unknown device action %s", tmp);
215                 return -1;
216         }
217
218         /* check that this is a usb message */
219         tmp = netlink_message_parse(buffer, len, "SUBSYSTEM");
220         if (!tmp || strcmp(tmp, "usb") != 0) {
221                 /* not usb. ignore */
222                 return -1;
223         }
224
225         /* check that this is an actual usb device */
226         tmp = netlink_message_parse(buffer, len, "DEVTYPE");
227         if (!tmp || strcmp(tmp, "usb_device") != 0) {
228                 /* not usb. ignore */
229                 return -1;
230         }
231
232         tmp = netlink_message_parse(buffer, len, "BUSNUM");
233         if (!tmp) {
234                 /* no bus number. try "DEVICE" */
235                 tmp = netlink_message_parse(buffer, len, "DEVICE");
236                 if (!tmp) {
237                         /* not usb. ignore */
238                         return -1;
239                 }
240
241                 /* Parse a device path such as /dev/bus/usb/003/004 */
242                 const char *pLastSlash = strrchr(tmp, '/');
243                 if (!pLastSlash)
244                         return -1;
245
246                 *busnum = (uint8_t)(strtoul(pLastSlash - 3, NULL, 10) & 0xff);
247                 if (errno) {
248                         errno = 0;
249                         return -1;
250                 }
251
252                 *devaddr = (uint8_t)(strtoul(pLastSlash + 1, NULL, 10) & 0xff);
253                 if (errno) {
254                         errno = 0;
255                         return -1;
256                 }
257
258                 return 0;
259         }
260
261         *busnum = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff);
262         if (errno) {
263                 errno = 0;
264                 return -1;
265         }
266
267         tmp = netlink_message_parse(buffer, len, "DEVNUM");
268         if (!tmp)
269                 return -1;
270
271         *devaddr = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff);
272         if (errno) {
273                 errno = 0;
274                 return -1;
275         }
276
277         tmp = netlink_message_parse(buffer, len, "DEVPATH");
278         if (!tmp)
279                 return -1;
280
281         for (i = strlen(tmp) - 1; i; --i) {
282                 if (tmp[i] == '/') {
283                         *sys_name = tmp + i + 1;
284                         break;
285                 }
286         }
287
288         /* found a usb device */
289         return 0;
290 }
291
292 static int linux_netlink_read_message(void)
293 {
294         char buffer[1024];
295         const char *sys_name = NULL;
296         uint8_t busnum, devaddr;
297         int detached, r;
298         ssize_t len;
299         struct iovec iov = { .iov_base = buffer, .iov_len = sizeof(buffer) };
300         struct msghdr meh = {
301                 .msg_iov = &iov, .msg_iovlen = 1,
302                 .msg_name = &snl, .msg_namelen = sizeof(snl)
303         };
304
305         /* read netlink message */
306         memset(buffer, 0, sizeof(buffer));
307         len = recvmsg(linux_netlink_socket, &meh, 0);
308         if (len < 32) {
309                 if (errno != EAGAIN)
310                         usbi_dbg("error receiving message from netlink");
311                 return -1;
312         }
313
314         /* TODO -- authenticate this message is from the kernel or udevd */
315
316         r = linux_netlink_parse(buffer, (size_t)len, &detached, &sys_name, &busnum, &devaddr);
317         if (r)
318                 return r;
319
320         usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s",
321                  busnum, devaddr, sys_name, detached ? "yes" : "no");
322
323         /* signal device is available (or not) to all contexts */
324         if (detached)
325                 linux_device_disconnected(busnum, devaddr);
326         else
327                 linux_hotplug_enumerate(busnum, devaddr, sys_name);
328
329         return 0;
330 }
331
332 static void *linux_netlink_event_thread_main(void *arg)
333 {
334         char dummy;
335         ssize_t r;
336         struct pollfd fds[] = {
337                 { .fd = netlink_control_pipe[0],
338                   .events = POLLIN },
339                 { .fd = linux_netlink_socket,
340                   .events = POLLIN },
341         };
342
343         UNUSED(arg);
344
345         usbi_dbg("netlink event thread entering");
346
347         while (poll(fds, 2, -1) >= 0) {
348                 if (fds[0].revents & POLLIN) {
349                         /* activity on control pipe, read the byte and exit */
350                         r = usbi_read(netlink_control_pipe[0], &dummy, sizeof(dummy));
351                         if (r <= 0)
352                                 usbi_warn(NULL, "netlink control pipe read failed");
353                         break;
354                 }
355                 if (fds[1].revents & POLLIN) {
356                         usbi_mutex_static_lock(&linux_hotplug_lock);
357                         linux_netlink_read_message();
358                         usbi_mutex_static_unlock(&linux_hotplug_lock);
359                 }
360         }
361
362         usbi_dbg("netlink event thread exiting");
363
364         return NULL;
365 }
366
367 void linux_netlink_hotplug_poll(void)
368 {
369         int r;
370
371         usbi_mutex_static_lock(&linux_hotplug_lock);
372         do {
373                 r = linux_netlink_read_message();
374         } while (r == 0);
375         usbi_mutex_static_unlock(&linux_hotplug_lock);
376 }