blob: d0cccd1113f90ed37aea8cc61b08383b27d281e0 [file] [log] [blame]
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001/*
Martin Szuleckicc4f7aa2014-10-02 23:35:00 +02002 * libusbmuxd.c
3 *
Nikias Bassena6b542b2019-05-22 11:32:11 +02004 * Copyright (C) 2009-2019 Nikias Bassen <nikias@gmx.li>
Martin Szuleckiae643b22014-10-06 17:51:53 +02005 * Copyright (C) 2009-2014 Martin Szulecki <m.szulecki@libimobiledevice.org>
Martin Szuleckicc4f7aa2014-10-02 23:35:00 +02006 * Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
Martin Szuleckicc4f7aa2014-10-02 23:35:00 +02007 *
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 */
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020022
Nikias Bassencad964c2018-09-15 02:44:27 +020023#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020026#include <stdint.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <stdio.h>
30#include <string.h>
31
Nikias Bassen75c79a62014-10-03 13:33:00 +020032#ifdef WIN32
33 #define USBMUXD_API __declspec( dllexport )
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +020034#else
Nikias Bassen75c79a62014-10-03 13:33:00 +020035 #ifdef HAVE_FVISIBILITY
36 #define USBMUXD_API __attribute__((visibility("default")))
37 #else
38 #define USBMUXD_API
39 #endif
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +020040#endif
41
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020042#ifndef EPROTO
43#define EPROTO 134
44#endif
45#ifndef EBADMSG
46#define EBADMSG 104
47#endif
Nikias Bassenc644d662019-02-26 23:39:31 +010048#ifndef ECONNREFUSED
49#define ECONNREFUSED 107
50#endif
David Crosby8e6ce392016-07-14 03:29:34 +020051
Nikias Bassen5b338a42017-01-03 18:04:37 +010052#include <unistd.h>
53#include <signal.h>
54
David Crosby8e6ce392016-07-14 03:29:34 +020055#ifdef WIN32
56#include <winsock2.h>
57#include <windows.h>
Nikias Bassen5b338a42017-01-03 18:04:37 +010058#ifndef HAVE_SLEEP
David Crosby8e6ce392016-07-14 03:29:34 +020059#define sleep(x) Sleep(x*1000)
Nikias Bassen5b338a42017-01-03 18:04:37 +010060#endif
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020061#else
62#include <sys/socket.h>
63#include <arpa/inet.h>
64#include <pthread.h>
Nikias Bassen21095ad2018-10-12 15:47:45 +020065#if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME) && !defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME_ERRNO_H)
Nikias Bassencad964c2018-09-15 02:44:27 +020066extern char *program_invocation_short_name;
67#endif
68#ifdef __APPLE__
69extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize);
70#include <sys/stat.h>
71#endif
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020072#endif
73
74#ifdef HAVE_INOTIFY
75#include <sys/inotify.h>
Nikias Bassen7430eec2019-05-25 17:59:40 +020076#include <sys/select.h>
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020077#define EVENT_SIZE (sizeof (struct inotify_event))
78#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
79#define USBMUXD_DIRNAME "/var/run"
80#define USBMUXD_SOCKET_NAME "usbmuxd"
Nikias Bassen7430eec2019-05-25 17:59:40 +020081static int use_inotify = 1;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020082#endif /* HAVE_INOTIFY */
83
Nikias Bassenf5a73872018-10-13 04:23:20 +020084#ifndef HAVE_STPNCPY
85static char* stpncpy(char *dst, const char *src, size_t len)
86{
87 size_t n = strlen(src);
88 if (n > len)
89 n = len;
90 return strncpy(dst, src, len) + n;
91}
92#endif
93
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020094#include <plist/plist.h>
Nikias Bassen3d43c4f2018-09-15 03:44:05 +020095#define PLIST_CLIENT_VERSION_STRING PACKAGE_STRING
Nikias Bassen82d1c572013-12-09 02:34:12 +010096#define PLIST_LIBUSBMUX_VERSION 3
Nikias Bassenc45ae1f2013-09-17 11:00:31 +020097
Nikias Bassencad964c2018-09-15 02:44:27 +020098static char *bundle_id = NULL;
99static char *prog_name = NULL;
100
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200101// usbmuxd public interface
102#include "usbmuxd.h"
103// usbmuxd protocol
104#include "usbmuxd-proto.h"
105// socket utility functions
Martin Szulecki9cc128a2014-02-10 21:22:59 +0100106#include "socket.h"
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200107// misc utility functions
108#include "collection.h"
Nikias Bassendc90b022019-02-27 00:50:09 +0100109// threads
110#include "thread.h"
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200111
Nikias Bassen5e6adfd2013-09-17 14:58:57 +0200112static int libusbmuxd_debug = 0;
Nikias Bassenf5a73872018-10-13 04:23:20 +0200113#ifndef PACKAGE
114#define PACKAGE "libusbmuxd"
115#endif
Nikias Bassen450c7152018-10-15 01:33:12 +0200116#define LIBUSBMUXD_DEBUG(level, format, ...) if (level <= libusbmuxd_debug) fprintf(stderr, ("[" PACKAGE "] " format), __VA_ARGS__); fflush(stderr);
117#define LIBUSBMUXD_ERROR(format, ...) LIBUSBMUXD_DEBUG(0, format, __VA_ARGS__)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200118
119static struct collection devices;
Nikias Bassendc90b022019-02-27 00:50:09 +0100120static THREAD_T devmon = THREAD_T_NULL;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200121static int listenfd = -1;
Nikias Bassen5304a312019-05-25 17:58:17 +0200122static int running = 0;
Nikias Bassena6b542b2019-05-22 11:32:11 +0200123static int cancelling = 0;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200124
Nikias Bassen232d27e2014-01-08 15:16:07 +0100125static volatile int use_tag = 0;
126static volatile int proto_version = 1;
127static volatile int try_list_devices = 1;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200128
Nikias Bassena6b542b2019-05-22 11:32:11 +0200129struct usbmuxd_subscription_context {
130 usbmuxd_event_cb_t callback;
131 void *user_data;
132};
133
134static struct usbmuxd_subscription_context *event_ctx = NULL;
135
136static struct collection listeners;
137thread_once_t listener_init_once = THREAD_ONCE_INIT;
138mutex_t listener_mutex;
139
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200140/**
141 * Finds a device info record by its handle.
142 * if the record is not found, NULL is returned.
143 */
144static usbmuxd_device_info_t *devices_find(uint32_t handle)
145{
146 FOREACH(usbmuxd_device_info_t *dev, &devices) {
147 if (dev && dev->handle == handle) {
148 return dev;
149 }
150 } ENDFOREACH
151 return NULL;
152}
153
154/**
155 * Creates a socket connection to usbmuxd.
156 * For Mac/Linux it is a unix domain socket,
157 * for Windows it is a tcp socket.
158 */
159static int connect_usbmuxd_socket()
160{
Nikias Bassen80fe6e82018-10-14 03:11:38 +0200161 char *usbmuxd_socket_addr = getenv("USBMUXD_SOCKET_ADDRESS");
162 if (usbmuxd_socket_addr) {
163 if (strncmp(usbmuxd_socket_addr, "UNIX:", 5) == 0) {
164#if defined(WIN32) || defined(__CYGWIN__)
165 /* not supported, ignore */
166#else
167 if (usbmuxd_socket_addr[5] != '\0') {
168 return socket_connect_unix(usbmuxd_socket_addr+5);
169 }
170#endif
171 } else {
172 uint16_t port = 0;
173 char *p = strrchr(usbmuxd_socket_addr, ':');
174 if (p) {
175 char *endp = NULL;
176 long l_port = strtol(p+1, &endp, 10);
177 if (endp && *endp == '\0') {
178 if (l_port > 0 && l_port < 65536) {
179 port = (uint16_t)l_port;
180 }
181 }
182 }
183 if (p && port > 0) {
184 char *connect_addr = NULL;
185 if (usbmuxd_socket_addr[0] == '[') {
186 connect_addr = strdup(usbmuxd_socket_addr+1);
187 connect_addr[p - usbmuxd_socket_addr - 1] = '\0';
188 p = strrchr(connect_addr, ']');
189 if (p) {
190 *p = '\0';
191 }
192 } else {
193 connect_addr = strdup(usbmuxd_socket_addr);
194 connect_addr[p - usbmuxd_socket_addr] = '\0';
195 }
196 if (connect_addr && *connect_addr != '\0') {
197 int res = socket_connect(connect_addr, port);
Nikias Bassen7430eec2019-05-25 17:59:40 +0200198#ifdef HAVE_INOTIFY
199 use_inotify = 0;
200#endif
Nikias Bassen80fe6e82018-10-14 03:11:38 +0200201 free(connect_addr);
202 return res;
203 }
204 free(connect_addr);
205 }
206 }
207 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200208#if defined(WIN32) || defined(__CYGWIN__)
Martin Szulecki9cc128a2014-02-10 21:22:59 +0100209 return socket_connect("127.0.0.1", USBMUXD_SOCKET_PORT);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200210#else
Martin Szulecki9cc128a2014-02-10 21:22:59 +0100211 return socket_connect_unix(USBMUXD_SOCKET_FILE);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200212#endif
213}
214
Nikias Bassen604c2502018-10-16 02:49:54 +0200215static void sanitize_udid(usbmuxd_device_info_t *devinfo)
216{
217 if (!devinfo)
218 return;
219 if (strlen(devinfo->udid) == 24) {
220 memmove(&devinfo->udid[9], &devinfo->udid[8], 17);
221 devinfo->udid[8] = '-';
222 }
223 if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) {
224 sprintf(devinfo->udid + 32, "%08x", devinfo->handle);
225 }
226}
227
Nikias Bassenf5a73872018-10-13 04:23:20 +0200228static usbmuxd_device_info_t *device_info_from_plist(plist_t props)
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100229{
Nikias Bassenf5a73872018-10-13 04:23:20 +0200230 usbmuxd_device_info_t* devinfo = NULL;
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100231 plist_t n = NULL;
232 uint64_t val = 0;
233 char *strval = NULL;
234
Nikias Bassenf5a73872018-10-13 04:23:20 +0200235 devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t));
236 if (!devinfo)
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100237 return NULL;
Nikias Bassenf5a73872018-10-13 04:23:20 +0200238 memset(devinfo, 0, sizeof(usbmuxd_device_info_t));
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100239
240 n = plist_dict_get_item(props, "DeviceID");
241 if (n && plist_get_node_type(n) == PLIST_UINT) {
242 plist_get_uint_val(n, &val);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200243 devinfo->handle = (uint32_t)val;
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100244 }
245
246 n = plist_dict_get_item(props, "ProductID");
247 if (n && plist_get_node_type(n) == PLIST_UINT) {
248 plist_get_uint_val(n, &val);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200249 devinfo->product_id = (uint32_t)val;
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100250 }
251
252 n = plist_dict_get_item(props, "SerialNumber");
253 if (n && plist_get_node_type(n) == PLIST_STRING) {
254 plist_get_string_val(n, &strval);
255 if (strval) {
Nikias Bassenf5a73872018-10-13 04:23:20 +0200256 char *t = stpncpy(devinfo->udid, strval, sizeof(devinfo->udid)-1);
257 *t = '\0';
Nikias Bassen604c2502018-10-16 02:49:54 +0200258 sanitize_udid(devinfo);
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100259 free(strval);
260 }
261 }
Nikias Bassenf5a73872018-10-13 04:23:20 +0200262
263 n = plist_dict_get_item(props, "ConnectionType");
264 if (n && plist_get_node_type(n) == PLIST_STRING) {
265 plist_get_string_val(n, &strval);
266 if (strval) {
267 if (strcmp(strval, "USB") == 0) {
268 devinfo->conn_type = CONNECTION_TYPE_USB;
269 } else if (strcmp(strval, "Network") == 0) {
270 devinfo->conn_type = CONNECTION_TYPE_NETWORK;
271 n = plist_dict_get_item(props, "NetworkAddress");
272 if (n && plist_get_node_type(n) == PLIST_DATA) {
273 char *netaddr = NULL;
274 uint64_t addr_len = 0;
275 plist_get_data_val(n, &netaddr, &addr_len);
276 if (netaddr && addr_len > 0 && addr_len < sizeof(devinfo->conn_data)) {
277 memcpy(devinfo->conn_data, netaddr, addr_len);
278 }
279 free(netaddr);
280 }
281 } else {
Nikias Bassen450c7152018-10-15 01:33:12 +0200282 LIBUSBMUXD_ERROR("%s: Unexpected ConnectionType '%s'\n", __func__, strval);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200283 }
284 free(strval);
285 }
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100286 }
287
Nikias Bassenf5a73872018-10-13 04:23:20 +0200288 if (!devinfo->udid[0]) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200289 LIBUSBMUXD_ERROR("%s: Failed to get SerialNumber (UDID)!\n", __func__);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200290 free(devinfo);
Nikias Bassen873252d2019-03-23 20:22:51 +0100291 return NULL;
Nikias Bassenf5a73872018-10-13 04:23:20 +0200292 }
293 if (!devinfo->conn_type) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200294 LIBUSBMUXD_ERROR("%s: Failed to get ConnectionType!\n", __func__);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200295 free(devinfo);
296 devinfo = NULL;
297 } else if (devinfo->conn_type == CONNECTION_TYPE_NETWORK && !devinfo->conn_data[0]) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200298 LIBUSBMUXD_ERROR("%s: Failed to get EscapedFullServiceName!\n", __func__);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200299 free(devinfo);
300 devinfo = NULL;
301 }
302
303 return devinfo;
304}
305
306static usbmuxd_device_info_t *device_info_from_device_record(struct usbmuxd_device_record *dev)
307{
308 if (!dev) {
309 return NULL;
310 }
311 usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t));
312 if (!devinfo) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200313 LIBUSBMUXD_ERROR("%s: Out of memory while allocating device info object\n", __func__);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200314 return NULL;
315 }
316
317 devinfo->handle = dev->device_id;
318 devinfo->product_id = dev->product_id;
319 char *t = stpncpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid)-2);
320 *t = '\0';
Nikias Bassen604c2502018-10-16 02:49:54 +0200321 sanitize_udid(devinfo);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200322
323 return devinfo;
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100324}
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100325
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200326static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload, int timeout)
327{
328 int recv_len;
329 struct usbmuxd_header hdr;
330 char *payload_loc = NULL;
331
332 header->length = 0;
333 header->version = 0;
334 header->message = 0;
335 header->tag = 0;
336
Martin Szulecki9cc128a2014-02-10 21:22:59 +0100337 recv_len = socket_receive_timeout(sfd, &hdr, sizeof(hdr), 0, timeout);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200338 if (recv_len < 0) {
Nikias Bassena6b542b2019-05-22 11:32:11 +0200339 if (!cancelling) {
340 LIBUSBMUXD_DEBUG(1, "%s: Error receiving packet: %s\n", __func__, strerror(-recv_len));
341 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200342 return recv_len;
343 } else if ((size_t)recv_len < sizeof(hdr)) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200344 LIBUSBMUXD_DEBUG(1, "%s: Received packet is too small, got %d bytes!\n", __func__, recv_len);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200345 return recv_len;
346 }
347
348 uint32_t payload_size = hdr.length - sizeof(hdr);
349 if (payload_size > 0) {
350 payload_loc = (char*)malloc(payload_size);
Nikias Bassen185eb2b2014-03-24 03:24:00 +0100351 uint32_t rsize = 0;
352 do {
353 int res = socket_receive_timeout(sfd, payload_loc + rsize, payload_size - rsize, 0, 5000);
354 if (res < 0) {
355 break;
356 }
357 rsize += res;
358 } while (rsize < payload_size);
359 if (rsize != payload_size) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200360 LIBUSBMUXD_DEBUG(1, "%s: Error receiving payload of size %d (bytes received: %d)\n", __func__, payload_size, rsize);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200361 free(payload_loc);
362 return -EBADMSG;
363 }
364 }
365
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200366 if (hdr.message == MESSAGE_PLIST) {
367 char *message = NULL;
368 plist_t plist = NULL;
369 plist_from_xml(payload_loc, payload_size, &plist);
370 free(payload_loc);
371
372 if (!plist) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200373 LIBUSBMUXD_DEBUG(1, "%s: Error getting plist from payload!\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200374 return -EBADMSG;
375 }
376
377 plist_t node = plist_dict_get_item(plist, "MessageType");
Nikias Bassen42802fb2013-12-11 21:36:04 +0100378 if (!node || plist_get_node_type(node) != PLIST_STRING) {
379 *payload = plist;
380 hdr.length = sizeof(hdr);
381 memcpy(header, &hdr, sizeof(hdr));
382 return hdr.length;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200383 }
Martin Szulecki9cc128a2014-02-10 21:22:59 +0100384
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200385 plist_get_string_val(node, &message);
386 if (message) {
387 uint64_t val = 0;
388 if (strcmp(message, "Result") == 0) {
389 /* result message */
390 uint32_t dwval = 0;
391 plist_t n = plist_dict_get_item(plist, "Number");
392 plist_get_uint_val(n, &val);
393 *payload = malloc(sizeof(uint32_t));
394 dwval = val;
395 memcpy(*payload, &dwval, sizeof(dwval));
396 hdr.length = sizeof(hdr) + sizeof(dwval);
397 hdr.message = MESSAGE_RESULT;
398 } else if (strcmp(message, "Attached") == 0) {
399 /* device add message */
Nikias Bassenf5a73872018-10-13 04:23:20 +0200400 usbmuxd_device_info_t *devinfo = NULL;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200401 plist_t props = plist_dict_get_item(plist, "Properties");
402 if (!props) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200403 LIBUSBMUXD_DEBUG(1, "%s: Could not get properties for message '%s' from plist!\n", __func__, message);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200404 free(message);
405 plist_free(plist);
406 return -EBADMSG;
407 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200408
Nikias Bassenf5a73872018-10-13 04:23:20 +0200409 devinfo = device_info_from_plist(props);
410 if (!devinfo) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200411 LIBUSBMUXD_DEBUG(1, "%s: Could not create device info object from properties!\n", __func__);
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100412 free(message);
413 plist_free(plist);
414 return -EBADMSG;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200415 }
Nikias Bassenf5a73872018-10-13 04:23:20 +0200416 *payload = (void*)devinfo;
417 hdr.length = sizeof(hdr) + sizeof(usbmuxd_device_info_t);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200418 hdr.message = MESSAGE_DEVICE_ADD;
419 } else if (strcmp(message, "Detached") == 0) {
420 /* device remove message */
421 uint32_t dwval = 0;
422 plist_t n = plist_dict_get_item(plist, "DeviceID");
423 if (n) {
424 plist_get_uint_val(n, &val);
425 *payload = malloc(sizeof(uint32_t));
426 dwval = val;
427 memcpy(*payload, &dwval, sizeof(dwval));
428 hdr.length = sizeof(hdr) + sizeof(dwval);
429 hdr.message = MESSAGE_DEVICE_REMOVE;
430 }
BALATON Zoltanfe6e9de2015-05-13 22:43:03 +0200431 } else if (strcmp(message, "Paired") == 0) {
432 /* device pair message */
433 uint32_t dwval = 0;
434 plist_t n = plist_dict_get_item(plist, "DeviceID");
435 if (n) {
436 plist_get_uint_val(n, &val);
437 *payload = malloc(sizeof(uint32_t));
438 dwval = val;
439 memcpy(*payload, &dwval, sizeof(dwval));
440 hdr.length = sizeof(hdr) + sizeof(dwval);
441 hdr.message = MESSAGE_DEVICE_PAIRED;
442 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200443 } else {
BALATON Zoltanfe6e9de2015-05-13 22:43:03 +0200444 char *xml = NULL;
445 uint32_t len = 0;
446 plist_to_xml(plist, &xml, &len);
Nikias Bassen450c7152018-10-15 01:33:12 +0200447 LIBUSBMUXD_DEBUG(1, "%s: Unexpected message '%s' in plist:\n%s\n", __func__, message, xml);
BALATON Zoltanfe6e9de2015-05-13 22:43:03 +0200448 free(xml);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200449 free(message);
450 plist_free(plist);
451 return -EBADMSG;
452 }
453 free(message);
454 }
455 plist_free(plist);
Nikias Bassenf5a73872018-10-13 04:23:20 +0200456 } else if (hdr.message == MESSAGE_DEVICE_ADD) {
457 usbmuxd_device_info_t *devinfo = device_info_from_device_record((struct usbmuxd_device_record*)payload_loc);
458 free(payload_loc);
459 *payload = devinfo;
Nikias Bassenb867ce92013-12-13 11:32:25 +0100460 } else {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200461 *payload = payload_loc;
462 }
463
464 memcpy(header, &hdr, sizeof(hdr));
465
466 return hdr.length;
467}
468
469/**
470 * Retrieves the result code to a previously sent request.
471 */
Nikias Bassen42802fb2013-12-11 21:36:04 +0100472static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t *result, void **result_plist)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200473{
474 struct usbmuxd_header hdr;
475 int recv_len;
476 uint32_t *res = NULL;
477
478 if (!result) {
479 return -EINVAL;
480 }
481 *result = -1;
Nikias Bassen42802fb2013-12-11 21:36:04 +0100482 if (result_plist) {
483 *result_plist = NULL;
484 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200485
BALATON Zoltan7f36c932016-07-12 22:00:11 +0200486 recv_len = receive_packet(sfd, &hdr, (void**)&res, 5000);
487 if (recv_len < 0 || (size_t)recv_len < sizeof(hdr)) {
488 free(res);
489 return (recv_len < 0 ? recv_len : -EPROTO);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200490 }
491
492 if (hdr.message == MESSAGE_RESULT) {
493 int ret = 0;
Nikias Bassen232d27e2014-01-08 15:16:07 +0100494 if (hdr.tag != tag) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200495 LIBUSBMUXD_DEBUG(1, "%s: WARNING: tag mismatch (%d != %d). Proceeding anyway.\n", __func__, hdr.tag, tag);
Nikias Bassen232d27e2014-01-08 15:16:07 +0100496 }
497 if (res) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200498 memcpy(result, res, sizeof(uint32_t));
499 ret = 1;
500 }
Nikias Bassen7b007832019-05-26 18:59:55 +0200501 free(res);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200502 return ret;
Nikias Bassenb867ce92013-12-13 11:32:25 +0100503 } else if (hdr.message == MESSAGE_PLIST) {
Nikias Bassen42802fb2013-12-11 21:36:04 +0100504 if (!result_plist) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200505 LIBUSBMUXD_DEBUG(1, "%s: MESSAGE_PLIST result but result_plist pointer is NULL!\n", __func__);
Nikias Bassen42802fb2013-12-11 21:36:04 +0100506 return -1;
507 }
508 *result_plist = (plist_t)res;
509 *result = RESULT_OK;
510 return 1;
511 }
Nikias Bassenb867ce92013-12-13 11:32:25 +0100512
Nikias Bassen450c7152018-10-15 01:33:12 +0200513 LIBUSBMUXD_DEBUG(1, "%s: Unexpected message of type %d received!\n", __func__, hdr.message);
Nikias Bassen7b007832019-05-26 18:59:55 +0200514 free(res);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200515 return -EPROTO;
516}
517
518static int send_packet(int sfd, uint32_t message, uint32_t tag, void *payload, uint32_t payload_size)
519{
520 struct usbmuxd_header header;
521
522 header.length = sizeof(struct usbmuxd_header);
523 header.version = proto_version;
524 header.message = message;
525 header.tag = tag;
526 if (payload && (payload_size > 0)) {
527 header.length += payload_size;
528 }
Martin Szulecki9cc128a2014-02-10 21:22:59 +0100529 int sent = socket_send(sfd, &header, sizeof(header));
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200530 if (sent != sizeof(header)) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200531 LIBUSBMUXD_DEBUG(1, "%s: ERROR: could not send packet header\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200532 return -1;
533 }
534 if (payload && (payload_size > 0)) {
Nikias Bassen185eb2b2014-03-24 03:24:00 +0100535 uint32_t ssize = 0;
536 do {
537 int res = socket_send(sfd, (char*)payload + ssize, payload_size - ssize);
538 if (res < 0) {
539 break;
540 }
541 ssize += res;
542 } while (ssize < payload_size);
543 sent += ssize;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200544 }
545 if (sent != (int)header.length) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200546 LIBUSBMUXD_DEBUG(1, "%s: ERROR: could not send whole packet (sent %d of %d)\n", __func__, sent, header.length);
Martin Szulecki9cc128a2014-02-10 21:22:59 +0100547 socket_close(sfd);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200548 return -1;
549 }
550 return sent;
551}
552
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100553static int send_plist_packet(int sfd, uint32_t tag, plist_t message)
554{
555 int res;
556 char *payload = NULL;
557 uint32_t payload_size = 0;
558
559 plist_to_xml(message, &payload, &payload_size);
560 res = send_packet(sfd, MESSAGE_PLIST, tag, payload, payload_size);
561 free(payload);
562
563 return res;
564}
565
Nikias Bassencad964c2018-09-15 02:44:27 +0200566static void get_bundle_id()
567{
568#if defined (__APPLE__)
569 char CONTENTS_INFO_PLIST[] = "Contents/Info.plist";
570 char* execpath = malloc(1024);
571 uint32_t size = 1024;
572 if (_NSGetExecutablePath(execpath, &size) != 0) {
573 free(execpath);
574 return;
575 }
576 // strip off executable name
577 char *p = execpath + strlen(execpath) - 1;
578 while (p > execpath && *p != '/') p--;
579 if (*p == '/') *p = '\0';
580 // now walk back trying to find "/Contents/MacOS", and strip it off
581 int macos_found = 0;
582 while (p > execpath) {
583 p--;
584 if (*p != '/') continue;
585 if (strcmp(p, "/.") == 0) {
586 *p = '\0';
587 } else if (!macos_found && strcmp(p, "/MacOS") == 0) {
588 *p = '\0';
589 macos_found++;
590 } else if (macos_found && strcmp(p, "/Contents") == 0) {
591 *p = '\0';
592 break;
593 } else {
594 break;
595 }
596 }
597 // now just append "/Contents/Info.plist"
598 size_t len = strlen(execpath) + sizeof(CONTENTS_INFO_PLIST) + 1;
599 char *infopl = malloc(len);
600 snprintf(infopl, len, "%s/%s", execpath, CONTENTS_INFO_PLIST);
601 free(execpath);
602 struct stat fst;
603 fst.st_size = 0;
604 if (stat(infopl, &fst) != 0) {
605 free(infopl);
606 return;
607 }
608 size_t fsize = fst.st_size;
609 if (fsize < 8) {
610 free(infopl);
611 return;
612 }
613 FILE *f = fopen(infopl, "r");
614 free(infopl);
615 if (!f)
616 return;
617 char *buf = malloc(fsize);
618 if (!buf)
619 return;
620 if (fread(buf, 1, fsize, f) == fsize) {
621 plist_t pl = NULL;
622 if (memcmp(buf, "bplist00", 8) == 0) {
623 plist_from_bin(buf, fst.st_size, &pl);
624 } else {
625 plist_from_xml(buf, fst.st_size, &pl);
626 }
627 if (pl) {
628 plist_t bid = plist_dict_get_item(pl, "CFBundleIdentifier");
629 if (plist_get_node_type(bid) == PLIST_STRING) {
630 plist_get_string_val(bid, &bundle_id);
631 }
632 plist_free(pl);
633 }
634 }
635 free(buf);
636 fclose(f);
637#endif
638}
639
640static void get_prog_name()
641{
642#if defined(__APPLE__) || defined(__FreeBSD__)
643 const char *pname = getprogname();
644 if (pname) {
645 prog_name = strdup(pname);
646 }
647#elif defined (WIN32)
648 TCHAR *_pname = malloc((MAX_PATH+1) * sizeof(TCHAR));
649 if (GetModuleFileName(NULL, _pname, MAX_PATH+1) > 0) {
650 char* pname = NULL;
651 #if defined(UNICODE) || defined(_UNICODE)
652 char* __pname = NULL;
653 int l = WideCharToMultiByte(CP_UTF8, 0, _pname, -1, NULL, 0, NULL, NULL);
654 if (l > 0) {
655 __pname = malloc(l);
656 if (WideCharToMultiByte(CP_UTF8, 0, _pname, -1, __pname, l, NULL, NULL) > 0) {
657 pname = __pname;
658 }
659 }
660 #else
661 pname = _pname;
662 #endif
663 if (pname) {
664 char *p = pname+strlen(pname)-1;
665 while (p > pname && *p != '\\' && *p != '/') p--;
666 if (*p == '\\' || *p == '/') p++;
667 prog_name = strdup(p);
668 }
669 #if defined(UNICODE) || defined(_UNICODE)
670 free(__pname);
671 #endif
672 }
673 free(_pname);
Nikias Bassen21095ad2018-10-12 15:47:45 +0200674#elif defined (HAVE_PROGRAM_INVOCATION_SHORT_NAME)
Nikias Bassencad964c2018-09-15 02:44:27 +0200675 char *pname = program_invocation_short_name;
676 if (pname) {
677 prog_name = strdup(pname);
678 }
679#elif defined (__linux__)
680 FILE *f = fopen("/proc/self/stat", "r");
681 if (!f) {
682 return;
683 }
684 char *tmpbuf = malloc(512);
685 size_t r = fread(tmpbuf, 1, 512, f);
686 if (r > 0) {
687 char *p = tmpbuf;
Nikias Bassenf5a73872018-10-13 04:23:20 +0200688 while ((p-tmpbuf < r) && (*p != '(') && (*p != '\0')) p++;
Nikias Bassencad964c2018-09-15 02:44:27 +0200689 if (*p == '(') {
690 p++;
691 char *pname = p;
Nikias Bassenf5a73872018-10-13 04:23:20 +0200692 while ((p-tmpbuf < r) && (*p != ')') && (*p != '\0')) p++;
Nikias Bassencad964c2018-09-15 02:44:27 +0200693 if (*p == ')') {
694 *p = '\0';
695 prog_name = strdup(pname);
696 }
697 }
698 }
699 free(tmpbuf);
700 fclose(f);
701#else
702 #warning FIXME: no method to determine program name
703#endif
704}
705
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100706static plist_t create_plist_message(const char* message_type)
707{
Nikias Bassencad964c2018-09-15 02:44:27 +0200708 if (!bundle_id) {
709 get_bundle_id();
710 }
711 if (!prog_name) {
712 get_prog_name();
713 }
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100714 plist_t plist = plist_new_dict();
Nikias Bassencad964c2018-09-15 02:44:27 +0200715 if (bundle_id) {
716 plist_dict_set_item(plist, "BundleID", plist_new_string(bundle_id));
717 }
Nikias Bassen79ce4292014-03-22 02:39:57 +0100718 plist_dict_set_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING));
719 plist_dict_set_item(plist, "MessageType", plist_new_string(message_type));
Nikias Bassencad964c2018-09-15 02:44:27 +0200720 if (prog_name) {
721 plist_dict_set_item(plist, "ProgName", plist_new_string(prog_name));
722 }
Nikias Bassen79ce4292014-03-22 02:39:57 +0100723 plist_dict_set_item(plist, "kLibUSBMuxVersion", plist_new_uint(PLIST_LIBUSBMUX_VERSION));
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100724 return plist;
725}
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100726
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200727static int send_listen_packet(int sfd, uint32_t tag)
728{
729 int res = 0;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200730 if (proto_version == 1) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200731 /* construct message plist */
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100732 plist_t plist = create_plist_message("Listen");
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200733
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100734 res = send_plist_packet(sfd, tag, plist);
735 plist_free(plist);
Nikias Bassenb867ce92013-12-13 11:32:25 +0100736 } else {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200737 /* binary packet */
738 res = send_packet(sfd, MESSAGE_LISTEN, tag, NULL, 0);
739 }
740 return res;
741}
742
743static int send_connect_packet(int sfd, uint32_t tag, uint32_t device_id, uint16_t port)
744{
745 int res = 0;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200746 if (proto_version == 1) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200747 /* construct message plist */
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100748 plist_t plist = create_plist_message("Connect");
Nikias Bassen79ce4292014-03-22 02:39:57 +0100749 plist_dict_set_item(plist, "DeviceID", plist_new_uint(device_id));
750 plist_dict_set_item(plist, "PortNumber", plist_new_uint(htons(port)));
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200751
Nikias Bassen779d0fd2013-12-09 02:18:00 +0100752 res = send_plist_packet(sfd, tag, plist);
753 plist_free(plist);
Nikias Bassenb867ce92013-12-13 11:32:25 +0100754 } else {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200755 /* binary packet */
756 struct {
757 uint32_t device_id;
758 uint16_t port;
759 uint16_t reserved;
760 } conninfo;
761
762 conninfo.device_id = device_id;
763 conninfo.port = htons(port);
764 conninfo.reserved = 0;
765
766 res = send_packet(sfd, MESSAGE_CONNECT, tag, &conninfo, sizeof(conninfo));
767 }
768 return res;
769}
770
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100771static int send_list_devices_packet(int sfd, uint32_t tag)
772{
773 int res = -1;
774
775 /* construct message plist */
776 plist_t plist = create_plist_message("ListDevices");
777
778 res = send_plist_packet(sfd, tag, plist);
779 plist_free(plist);
780
781 return res;
782}
Nikias Bassen01cb5da2013-12-12 01:30:04 +0100783
Nikias Bassen75534a52013-12-13 11:59:32 +0100784static int send_read_buid_packet(int sfd, uint32_t tag)
785{
786 int res = -1;
787
788 /* construct message plist */
789 plist_t plist = create_plist_message("ReadBUID");
790
791 res = send_plist_packet(sfd, tag, plist);
792 plist_free(plist);
793
794 return res;
795}
796
Nikias Bassen07a493a2018-09-15 03:44:58 +0200797static int send_pair_record_packet(int sfd, uint32_t tag, const char* msgtype, const char* pair_record_id, uint32_t device_id, plist_t data)
Nikias Bassen30b64de2013-12-13 12:01:45 +0100798{
799 int res = -1;
800
801 /* construct message plist */
802 plist_t plist = create_plist_message(msgtype);
Nikias Bassen79ce4292014-03-22 02:39:57 +0100803 plist_dict_set_item(plist, "PairRecordID", plist_new_string(pair_record_id));
Nikias Bassen30b64de2013-12-13 12:01:45 +0100804 if (data) {
Nikias Bassen79ce4292014-03-22 02:39:57 +0100805 plist_dict_set_item(plist, "PairRecordData", plist_copy(data));
Nikias Bassen30b64de2013-12-13 12:01:45 +0100806 }
Nikias Bassen07a493a2018-09-15 03:44:58 +0200807 if (device_id > 0) {
808 plist_dict_set_item(plist, "DeviceID", plist_new_uint(device_id));
809 }
Martin Szulecki4a6182b2020-06-08 22:53:58 +0200810
Nikias Bassen30b64de2013-12-13 12:01:45 +0100811 res = send_plist_packet(sfd, tag, plist);
812 plist_free(plist);
813
814 return res;
815}
816
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200817/**
818 * Generates an event, i.e. calls the callback function.
819 * A reference to a populated usbmuxd_event_t with information about the event
820 * and the corresponding device will be passed to the callback function.
821 */
Nikias Bassena6b542b2019-05-22 11:32:11 +0200822static void generate_event(const usbmuxd_device_info_t *dev, enum usbmuxd_event_type event)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200823{
824 usbmuxd_event_t ev;
825
Nikias Bassena6b542b2019-05-22 11:32:11 +0200826 if (!dev) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200827 return;
828 }
829
830 ev.event = event;
831 memcpy(&ev.device, dev, sizeof(usbmuxd_device_info_t));
832
Nikias Bassena6b542b2019-05-22 11:32:11 +0200833 mutex_lock(&listener_mutex);
834 FOREACH(struct usbmuxd_subscription_context* context, &listeners) {
835 context->callback(&ev, context->user_data);
836 } ENDFOREACH
837 mutex_unlock(&listener_mutex);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200838}
839
840static int usbmuxd_listen_poll()
841{
842 int sfd;
843
844 sfd = connect_usbmuxd_socket();
845 if (sfd < 0) {
Nikias Bassena6b542b2019-05-22 11:32:11 +0200846 while (1) {
847 mutex_lock(&listener_mutex);
848 int num = collection_count(&listeners);
849 mutex_unlock(&listener_mutex);
850 if (num <= 0) {
851 break;
852 }
Nikias Bassen471b2262014-10-10 21:51:48 +0200853 if ((sfd = connect_usbmuxd_socket()) >= 0) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200854 break;
855 }
856 sleep(1);
857 }
858 }
859
860 return sfd;
861}
862
863#ifdef HAVE_INOTIFY
Nikias Bassen7430eec2019-05-25 17:59:40 +0200864#ifndef HAVE_PSELECT
865static int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)
866{
867 int ready;
868 struct timeval tv;
869 struct timeval *p_timeout;
870 sigset_t origmask;
871
872 if (timeout) {
873 tv.tv_sec = timeout->tv_sec;
874 tv.tv_usec = timeout->tv_nsec / 1000;
875 p_timeout = &tv;
876 } else {
877 p_timeout = NULL;
878 }
879
880 pthread_sigmask(SIG_SETMASK, sigmask, &origmask);
881 ready = select(nfds, readfds, writefds, exceptfds, p_timeout);
882 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
883
884 return ready;
885}
886#endif
887
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200888static int usbmuxd_listen_inotify()
889{
890 int inot_fd;
891 int watch_d;
892 int sfd;
893
894 if (!use_inotify) {
895 return -2;
896 }
897
898 sfd = connect_usbmuxd_socket();
899 if (sfd >= 0)
900 return sfd;
901
902 sfd = -1;
903 inot_fd = inotify_init ();
904 if (inot_fd < 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200905 LIBUSBMUXD_DEBUG(1, "%s: Failed to setup inotify\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200906 return -2;
907 }
908
909 /* inotify is setup, listen for events that concern us */
910 watch_d = inotify_add_watch (inot_fd, USBMUXD_DIRNAME, IN_CREATE);
911 if (watch_d < 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200912 LIBUSBMUXD_DEBUG(1, "%s: Failed to setup watch descriptor for socket dir\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200913 close (inot_fd);
914 return -2;
915 }
916
917 while (1) {
Nikias Bassen7430eec2019-05-25 17:59:40 +0200918 fd_set rfds;
919 struct timespec tv = {1, 0};
920
921 FD_ZERO(&rfds);
922 FD_SET(inot_fd, &rfds);
923 int r = pselect(inot_fd+1, &rfds, NULL, NULL, &tv, NULL);
924 if (r < 0) {
925 break;
926 } else if (r == 0) {
927 continue;
928 }
929
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200930 ssize_t len, i;
931 char buff[EVENT_BUF_LEN] = {0};
932
933 i = 0;
934 len = read (inot_fd, buff, EVENT_BUF_LEN -1);
935 if (len < 0)
936 goto end;
937 while (i < len) {
938 struct inotify_event *pevent = (struct inotify_event *) & buff[i];
939
940 /* check that it's ours */
941 if (pevent->mask & IN_CREATE &&
Martin Szulecki4a6182b2020-06-08 22:53:58 +0200942 pevent->len &&
943 pevent->name[0] != 0 &&
944 strcmp(pevent->name, USBMUXD_SOCKET_NAME) == 0) {
Nikias Bassenef914f12014-10-10 21:53:07 +0200945 /* retry if usbmuxd isn't ready yet */
946 int retry = 10;
947 while (--retry >= 0) {
948 if ((sfd = connect_usbmuxd_socket ()) >= 0) {
949 break;
950 }
951 sleep(1);
952 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200953 goto end;
954 }
955 i += EVENT_SIZE + pevent->len;
956 }
957 }
958
959end:
960 inotify_rm_watch(inot_fd, watch_d);
961 close(inot_fd);
962
963 return sfd;
964}
965#endif /* HAVE_INOTIFY */
966
967/**
968 * Tries to connect to usbmuxd and wait if it is not running.
969 */
970static int usbmuxd_listen()
971{
972 int sfd;
973 uint32_t res = -1;
Nikias Bassen232d27e2014-01-08 15:16:07 +0100974 int tag;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200975
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200976retry:
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200977
978#ifdef HAVE_INOTIFY
979 sfd = usbmuxd_listen_inotify();
980 if (sfd == -2)
981 sfd = usbmuxd_listen_poll();
982#else
983 sfd = usbmuxd_listen_poll();
984#endif
985
986 if (sfd < 0) {
Nikias Bassen7430eec2019-05-25 17:59:40 +0200987 if (!cancelling) {
988 LIBUSBMUXD_DEBUG(1, "%s: ERROR: usbmuxd was supposed to be running here...\n", __func__);
989 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200990 return sfd;
991 }
992
Nikias Bassen232d27e2014-01-08 15:16:07 +0100993 tag = ++use_tag;
Nikias Bassen232d27e2014-01-08 15:16:07 +0100994 if (send_listen_packet(sfd, tag) <= 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +0200995 LIBUSBMUXD_DEBUG(1, "%s: ERROR: could not send listen packet\n", __func__);
Martin Szulecki9cc128a2014-02-10 21:22:59 +0100996 socket_close(sfd);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +0200997 return -1;
998 }
Nikias Bassen4e4dec22014-03-24 03:07:19 +0100999 if ((usbmuxd_get_result(sfd, tag, &res, NULL) == 1) && (res != 0)) {
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001000 socket_close(sfd);
Nikias Bassen090492d2013-12-11 21:04:53 +01001001 if ((res == RESULT_BADVERSION) && (proto_version == 1)) {
1002 proto_version = 0;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001003 goto retry;
1004 }
Nikias Bassen450c7152018-10-15 01:33:12 +02001005 LIBUSBMUXD_DEBUG(1, "%s: ERROR: did not get OK but %d\n", __func__, res);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001006 return -1;
1007 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001008 return sfd;
1009}
1010
1011/**
1012 * Waits for an event to occur, i.e. a packet coming from usbmuxd.
1013 * Calls generate_event to pass the event via callback to the client program.
1014 */
Nikias Bassena6b542b2019-05-22 11:32:11 +02001015static int get_next_event(int sfd)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001016{
1017 struct usbmuxd_header hdr;
1018 void *payload = NULL;
1019
1020 /* block until we receive something */
1021 if (receive_packet(sfd, &hdr, &payload, 0) < 0) {
Nikias Bassena6b542b2019-05-22 11:32:11 +02001022 if (!cancelling) {
1023 LIBUSBMUXD_DEBUG(1, "%s: Error in usbmuxd connection, disconnecting all devices!\n", __func__);
1024 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001025 // when then usbmuxd connection fails,
1026 // generate remove events for every device that
1027 // is still present so applications know about it
1028 FOREACH(usbmuxd_device_info_t *dev, &devices) {
Nikias Bassena6b542b2019-05-22 11:32:11 +02001029 generate_event(dev, UE_DEVICE_REMOVE);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001030 collection_remove(&devices, dev);
1031 free(dev);
1032 } ENDFOREACH
1033 return -EIO;
1034 }
1035
1036 if ((hdr.length > sizeof(hdr)) && !payload) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001037 LIBUSBMUXD_DEBUG(1, "%s: Invalid packet received, payload is missing!\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001038 return -EBADMSG;
1039 }
1040
1041 if (hdr.message == MESSAGE_DEVICE_ADD) {
Nikias Bassenf5a73872018-10-13 04:23:20 +02001042 usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)payload;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001043 collection_add(&devices, devinfo);
Nikias Bassena6b542b2019-05-22 11:32:11 +02001044 generate_event(devinfo, UE_DEVICE_ADD);
Nikias Bassenf5a73872018-10-13 04:23:20 +02001045 payload = NULL;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001046 } else if (hdr.message == MESSAGE_DEVICE_REMOVE) {
1047 uint32_t handle;
1048 usbmuxd_device_info_t *devinfo;
1049
1050 memcpy(&handle, payload, sizeof(uint32_t));
1051
1052 devinfo = devices_find(handle);
1053 if (!devinfo) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001054 LIBUSBMUXD_DEBUG(1, "%s: WARNING: got device remove message for handle %d, but couldn't find the corresponding handle in the device list. This event will be ignored.\n", __func__, handle);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001055 } else {
Nikias Bassena6b542b2019-05-22 11:32:11 +02001056 generate_event(devinfo, UE_DEVICE_REMOVE);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001057 collection_remove(&devices, devinfo);
1058 free(devinfo);
1059 }
Nikias Bassen75c6f682016-07-14 03:09:44 +02001060 } else if (hdr.message == MESSAGE_DEVICE_PAIRED) {
1061 uint32_t handle;
1062 usbmuxd_device_info_t *devinfo;
1063
1064 memcpy(&handle, payload, sizeof(uint32_t));
1065
1066 devinfo = devices_find(handle);
1067 if (!devinfo) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001068 LIBUSBMUXD_DEBUG(1, "%s: WARNING: got paired message for device handle %d, but couldn't find the corresponding handle in the device list. This event will be ignored.\n", __func__, handle);
Nikias Bassen75c6f682016-07-14 03:09:44 +02001069 } else {
Nikias Bassena6b542b2019-05-22 11:32:11 +02001070 generate_event(devinfo, UE_DEVICE_PAIRED);
Nikias Bassen75c6f682016-07-14 03:09:44 +02001071 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001072 } else if (hdr.length > 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001073 LIBUSBMUXD_DEBUG(1, "%s: Unexpected message type %d length %d received!\n", __func__, hdr.message, hdr.length);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001074 }
Nikias Bassen7b007832019-05-26 18:59:55 +02001075 free(payload);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001076 return 0;
1077}
1078
1079static void device_monitor_cleanup(void* data)
1080{
1081 FOREACH(usbmuxd_device_info_t *dev, &devices) {
1082 collection_remove(&devices, dev);
1083 free(dev);
1084 } ENDFOREACH
1085 collection_free(&devices);
1086
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001087 socket_close(listenfd);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001088 listenfd = -1;
1089}
1090
1091/**
1092 * Device Monitor thread function.
1093 *
1094 * This function sets up a connection to usbmuxd
1095 */
1096static void *device_monitor(void *data)
1097{
Nikias Bassen5304a312019-05-25 17:58:17 +02001098 running = 1;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001099 collection_init(&devices);
Nikias Bassena6b542b2019-05-22 11:32:11 +02001100 cancelling = 0;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001101
Nikias Bassendc90b022019-02-27 00:50:09 +01001102#ifdef HAVE_THREAD_CLEANUP
1103 thread_cleanup_push(device_monitor_cleanup, NULL);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001104#endif
Nikias Bassena6b542b2019-05-22 11:32:11 +02001105 do {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001106
1107 listenfd = usbmuxd_listen();
1108 if (listenfd < 0) {
1109 continue;
1110 }
1111
Nikias Bassena6b542b2019-05-22 11:32:11 +02001112 while (running) {
1113 int res = get_next_event(listenfd);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001114 if (res < 0) {
Martin Szulecki4a6182b2020-06-08 22:53:58 +02001115 break;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001116 }
1117 }
Nikias Bassena6b542b2019-05-22 11:32:11 +02001118
1119 mutex_lock(&listener_mutex);
1120 if (collection_count(&listeners) == 0) {
1121 running = 0;
1122 }
1123 mutex_unlock(&listener_mutex);
1124 } while (running);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001125
Nikias Bassendc90b022019-02-27 00:50:09 +01001126#ifdef HAVE_THREAD_CLEANUP
1127 thread_cleanup_pop(1);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001128#else
1129 device_monitor_cleanup(NULL);
1130#endif
Nikias Bassena6b542b2019-05-22 11:32:11 +02001131
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001132 return NULL;
1133}
1134
Nikias Bassena6b542b2019-05-22 11:32:11 +02001135static void init_listeners(void)
1136{
1137 collection_init(&listeners);
1138 mutex_init(&listener_mutex);
1139}
1140
1141USBMUXD_API int usbmuxd_events_subscribe(usbmuxd_subscription_context_t *ctx, usbmuxd_event_cb_t callback, void *user_data)
1142{
1143 if (!ctx || !callback) {
1144 return -EINVAL;
1145 }
1146
1147 thread_once(&listener_init_once, init_listeners);
1148
1149 mutex_lock(&listener_mutex);
1150 *ctx = malloc(sizeof(struct usbmuxd_subscription_context));
1151 if (!*ctx) {
1152 mutex_unlock(&listener_mutex);
1153 LIBUSBMUXD_ERROR("ERROR: %s: malloc failed\n", __func__);
1154 return -ENOMEM;
1155 }
1156 (*ctx)->callback = callback;
1157 (*ctx)->user_data = user_data;
1158
1159 collection_add(&listeners, *ctx);
1160
1161 if (devmon == THREAD_T_NULL || !thread_alive(devmon)) {
1162 mutex_unlock(&listener_mutex);
1163 int res = thread_new(&devmon, device_monitor, NULL);
1164 if (res != 0) {
1165 free(*ctx);
1166 LIBUSBMUXD_DEBUG(1, "%s: ERROR: Could not start device watcher thread!\n", __func__);
1167 return res;
1168 }
1169 } else {
1170 /* we need to submit DEVICE_ADD events to the new listener */
1171 FOREACH(usbmuxd_device_info_t *dev, &devices) {
1172 if (dev) {
1173 usbmuxd_event_t ev;
1174 ev.event = UE_DEVICE_ADD;
1175 memcpy(&ev.device, dev, sizeof(usbmuxd_device_info_t));
1176 (*ctx)->callback(&ev, (*ctx)->user_data);
1177 }
1178 } ENDFOREACH
1179 mutex_unlock(&listener_mutex);
1180 }
1181
1182 return 0;
1183}
1184
1185USBMUXD_API int usbmuxd_events_unsubscribe(usbmuxd_subscription_context_t ctx)
1186{
1187 int ret = 0;
1188 int num = 0;
1189
1190 if (!ctx) {
1191 return -EINVAL;
1192 }
1193
1194 mutex_lock(&listener_mutex);
1195 if (collection_remove(&listeners, ctx) == 0) {
Nikias Bassen8b82ef12019-05-23 01:16:29 +02001196 FOREACH(usbmuxd_device_info_t *dev, &devices) {
1197 if (dev) {
1198 usbmuxd_event_t ev;
1199 ev.event = UE_DEVICE_REMOVE;
1200 memcpy(&ev.device, dev, sizeof(usbmuxd_device_info_t));
1201 (ctx)->callback(&ev, (ctx)->user_data);
1202 }
1203 } ENDFOREACH
Nikias Bassena6b542b2019-05-22 11:32:11 +02001204 free(ctx);
1205 }
1206 num = collection_count(&listeners);
1207 mutex_unlock(&listener_mutex);
1208
1209 if (num == 0) {
1210 int res = 0;
1211 cancelling = 1;
1212 socket_shutdown(listenfd, SHUT_RDWR);
1213 if (thread_alive(devmon)) {
Nikias Bassen5304a312019-05-25 17:58:17 +02001214 if (thread_cancel(devmon) < 0) {
1215 running = 0;
1216 }
Nikias Bassen7430eec2019-05-25 17:59:40 +02001217#if defined(HAVE_INOTIFY) && !defined(HAVE_PTHREAD_CANCEL)
1218 pthread_kill(devmon, SIGINT);
1219#endif
Nikias Bassena6b542b2019-05-22 11:32:11 +02001220 res = thread_join(devmon);
1221 thread_free(devmon);
1222 devmon = THREAD_T_NULL;
1223 }
1224 if ((res != 0) && (res != ESRCH)) {
1225 ret = res;
1226 }
1227 }
1228
1229 return ret;
1230}
1231
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001232USBMUXD_API int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001233{
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001234 if (!callback) {
1235 return -EINVAL;
1236 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001237
Nikias Bassena6b542b2019-05-22 11:32:11 +02001238 if (event_ctx) {
1239 usbmuxd_events_unsubscribe(event_ctx);
1240 event_ctx = NULL;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001241 }
Nikias Bassena6b542b2019-05-22 11:32:11 +02001242 return usbmuxd_events_subscribe(&event_ctx, callback, user_data);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001243}
1244
fidetro98f2f232020-04-19 00:01:00 +08001245USBMUXD_API int usbmuxd_unsubscribe(void)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001246{
Nikias Bassena6b542b2019-05-22 11:32:11 +02001247 int res = usbmuxd_events_unsubscribe(event_ctx);
1248 event_ctx = NULL;
1249 return res;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001250}
1251
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001252USBMUXD_API int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001253{
1254 int sfd;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001255 int tag;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001256 int listen_success = 0;
1257 uint32_t res;
1258 struct collection tmpdevs;
1259 usbmuxd_device_info_t *newlist = NULL;
1260 struct usbmuxd_header hdr;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001261 int dev_cnt = 0;
1262 void *payload = NULL;
1263
1264 *device_list = NULL;
1265
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001266retry:
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001267 sfd = connect_usbmuxd_socket();
1268 if (sfd < 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001269 LIBUSBMUXD_DEBUG(1, "%s: error opening socket!\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001270 return sfd;
1271 }
1272
Nikias Bassen232d27e2014-01-08 15:16:07 +01001273 tag = ++use_tag;
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001274 if ((proto_version == 1) && (try_list_devices)) {
Nikias Bassen232d27e2014-01-08 15:16:07 +01001275 if (send_list_devices_packet(sfd, tag) > 0) {
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001276 plist_t list = NULL;
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001277 if ((usbmuxd_get_result(sfd, tag, &res, &list) == 1) && (res == 0)) {
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001278 plist_t devlist = plist_dict_get_item(list, "DeviceList");
1279 if (devlist && plist_get_node_type(devlist) == PLIST_ARRAY) {
1280 collection_init(&tmpdevs);
1281 uint32_t numdevs = plist_array_get_size(devlist);
1282 uint32_t i;
1283 for (i = 0; i < numdevs; i++) {
1284 plist_t pdev = plist_array_get_item(devlist, i);
1285 plist_t props = plist_dict_get_item(pdev, "Properties");
Nikias Bassenf5a73872018-10-13 04:23:20 +02001286 usbmuxd_device_info_t *devinfo = device_info_from_plist(props);
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001287 if (!devinfo) {
Nikias Bassen23276782014-05-06 20:49:51 +02001288 socket_close(sfd);
Nikias Bassen450c7152018-10-15 01:33:12 +02001289 LIBUSBMUXD_DEBUG(1, "%s: Could not create device info object from properties!\n", __func__);
Nikias Bassend2eb6802014-03-02 03:32:54 +01001290 plist_free(list);
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001291 return -1;
1292 }
1293 collection_add(&tmpdevs, devinfo);
1294 }
Nikias Bassend2eb6802014-03-02 03:32:54 +01001295 plist_free(list);
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001296 goto got_device_list;
1297 }
1298 } else {
1299 if (res == RESULT_BADVERSION) {
1300 proto_version = 0;
1301 }
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001302 socket_close(sfd);
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001303 try_list_devices = 0;
Nikias Bassend2eb6802014-03-02 03:32:54 +01001304 plist_free(list);
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001305 goto retry;
1306 }
Nikias Bassend2eb6802014-03-02 03:32:54 +01001307 plist_free(list);
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001308 }
1309 }
Nikias Bassenb867ce92013-12-13 11:32:25 +01001310
Nikias Bassen232d27e2014-01-08 15:16:07 +01001311 tag = ++use_tag;
1312 if (send_listen_packet(sfd, tag) > 0) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001313 res = -1;
1314 // get response
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001315 if ((usbmuxd_get_result(sfd, tag, &res, NULL) == 1) && (res == 0)) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001316 listen_success = 1;
1317 } else {
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001318 socket_close(sfd);
Nikias Bassen090492d2013-12-11 21:04:53 +01001319 if ((res == RESULT_BADVERSION) && (proto_version == 1)) {
1320 proto_version = 0;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001321 goto retry;
1322 }
Nikias Bassen450c7152018-10-15 01:33:12 +02001323 LIBUSBMUXD_DEBUG(1, "%s: Did not get response to scan request (with result=0)...\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001324 return res;
1325 }
1326 }
1327
1328 if (!listen_success) {
Nikias Bassen23276782014-05-06 20:49:51 +02001329 socket_close(sfd);
Nikias Bassen450c7152018-10-15 01:33:12 +02001330 LIBUSBMUXD_DEBUG(1, "%s: Could not send listen request!\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001331 return -1;
1332 }
1333
1334 collection_init(&tmpdevs);
1335
1336 // receive device list
1337 while (1) {
Nikias Bassen4d717ca2013-09-17 13:12:12 +02001338 if (receive_packet(sfd, &hdr, &payload, 100) > 0) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001339 if (hdr.message == MESSAGE_DEVICE_ADD) {
Nikias Bassenf5a73872018-10-13 04:23:20 +02001340 usbmuxd_device_info_t *devinfo = payload;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001341 collection_add(&tmpdevs, devinfo);
Nikias Bassenf5a73872018-10-13 04:23:20 +02001342 payload = NULL;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001343 } else if (hdr.message == MESSAGE_DEVICE_REMOVE) {
1344 uint32_t handle;
1345 usbmuxd_device_info_t *devinfo = NULL;
1346
1347 memcpy(&handle, payload, sizeof(uint32_t));
1348
1349 FOREACH(usbmuxd_device_info_t *di, &tmpdevs) {
1350 if (di && di->handle == handle) {
1351 devinfo = di;
1352 break;
1353 }
1354 } ENDFOREACH
1355 if (devinfo) {
1356 collection_remove(&tmpdevs, devinfo);
1357 free(devinfo);
1358 }
1359 } else {
Nikias Bassen450c7152018-10-15 01:33:12 +02001360 LIBUSBMUXD_DEBUG(1, "%s: Unexpected message %d\n", __func__, hdr.message);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001361 }
Nikias Bassen7b007832019-05-26 18:59:55 +02001362 free(payload);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001363 } else {
1364 // we _should_ have all of them now.
1365 // or perhaps an error occured.
1366 break;
1367 }
1368 }
Nikias Bassenb867ce92013-12-13 11:32:25 +01001369
Nikias Bassen01cb5da2013-12-12 01:30:04 +01001370got_device_list:
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001371
1372 // explicitly close connection
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001373 socket_close(sfd);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001374
1375 // create copy of device info entries from collection
1376 newlist = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t) * (collection_count(&tmpdevs) + 1));
1377 dev_cnt = 0;
1378 FOREACH(usbmuxd_device_info_t *di, &tmpdevs) {
1379 if (di) {
1380 memcpy(&newlist[dev_cnt], di, sizeof(usbmuxd_device_info_t));
1381 free(di);
1382 dev_cnt++;
1383 }
1384 } ENDFOREACH
1385 collection_free(&tmpdevs);
1386
1387 memset(&newlist[dev_cnt], 0, sizeof(usbmuxd_device_info_t));
1388 *device_list = newlist;
1389
1390 return dev_cnt;
1391}
1392
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001393USBMUXD_API int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001394{
1395 if (device_list) {
1396 free(*device_list);
1397 }
1398 return 0;
1399}
1400
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001401USBMUXD_API int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001402{
1403 usbmuxd_device_info_t *dev_list = NULL;
Nikias Bassenf5a73872018-10-13 04:23:20 +02001404 usbmuxd_device_info_t *dev = NULL;
1405 int result = 0;
1406 int i;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001407
1408 if (!device) {
1409 return -EINVAL;
1410 }
1411 if (usbmuxd_get_device_list(&dev_list) < 0) {
1412 return -ENODEV;
1413 }
1414
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001415 for (i = 0; dev_list[i].handle > 0; i++) {
1416 if (!udid) {
Nikias Bassenf5a73872018-10-13 04:23:20 +02001417 if (dev_list[i].conn_type == CONNECTION_TYPE_USB) {
1418 dev = &dev_list[i];
1419 break;
1420 }
1421 } else if (!strcmp(udid, dev_list[i].udid)) {
1422 if (dev_list[i].conn_type == CONNECTION_TYPE_USB) {
1423 dev = &dev_list[i];
1424 break;
1425 }
1426 }
1427 }
1428
1429 if (dev) {
1430 device->handle = dev->handle;
1431 device->product_id = dev->product_id;
1432 char *t = stpncpy(device->udid, dev->udid, sizeof(device->udid)-1);
1433 *t = '\0';
1434 device->conn_type = dev->conn_type;
1435 memcpy(device->conn_data, dev->conn_data, sizeof(device->conn_data));
1436 result = 1;
1437 }
1438
1439 usbmuxd_device_list_free(&dev_list);
1440
1441 return result;
1442}
1443
1444USBMUXD_API int usbmuxd_get_device(const char *udid, usbmuxd_device_info_t *device, enum usbmux_lookup_options options)
1445{
1446 usbmuxd_device_info_t *dev_list = NULL;
1447 usbmuxd_device_info_t *dev_network = NULL;
1448 usbmuxd_device_info_t *dev_usbmuxd = NULL;
1449 usbmuxd_device_info_t *dev = NULL;
1450 int result = 0;
1451 int i;
1452
1453 if (!device) {
1454 return -EINVAL;
1455 }
1456 if (usbmuxd_get_device_list(&dev_list) < 0) {
1457 return -ENODEV;
1458 }
1459
1460 if (options == 0) {
1461 options = DEVICE_LOOKUP_USBMUX;
1462 }
1463
1464 for (i = 0; dev_list[i].handle > 0; i++) {
1465 if (!udid) {
1466 if ((options & DEVICE_LOOKUP_USBMUX) && (dev_list[i].conn_type == CONNECTION_TYPE_USB)) {
1467 dev_usbmuxd = &dev_list[i];
1468 break;
1469 } else if ((options & DEVICE_LOOKUP_NETWORK) && (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK)) {
1470 dev_network = &dev_list[i];
1471 break;
1472 }
1473 } else if (!strcmp(udid, dev_list[i].udid)) {
1474 if ((options & DEVICE_LOOKUP_USBMUX) && (dev_list[i].conn_type == CONNECTION_TYPE_USB)) {
1475 dev_usbmuxd = &dev_list[i];
1476 } else if ((options & DEVICE_LOOKUP_NETWORK) && (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK)) {
1477 dev_network = &dev_list[i];
1478 }
1479 }
1480 if (dev_usbmuxd && dev_network) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001481 break;
1482 }
Nikias Bassenf5a73872018-10-13 04:23:20 +02001483 }
1484
1485 if (dev_network && dev_usbmuxd) {
1486 dev = (options & DEVICE_LOOKUP_PREFER_NETWORK) ? dev_network : dev_usbmuxd;
1487 } else if (dev_network) {
1488 dev = dev_network;
1489 } else if (dev_usbmuxd) {
1490 dev = dev_usbmuxd;
1491 }
1492
1493 if (dev) {
1494 device->handle = dev->handle;
1495 device->product_id = dev->product_id;
1496 char *t = stpncpy(device->udid, dev->udid, sizeof(device->udid)-1);
1497 *t = '\0';
1498 device->conn_type = dev->conn_type;
1499 memcpy(device->conn_data, dev->conn_data, sizeof(device->conn_data));
1500 result = 1;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001501 }
1502
1503 free(dev_list);
1504
1505 return result;
1506}
1507
Nikias Bassenf5a73872018-10-13 04:23:20 +02001508USBMUXD_API int usbmuxd_connect(const uint32_t handle, const unsigned short port)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001509{
1510 int sfd;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001511 int tag;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001512 int connected = 0;
Nikias Bassenc75605d2019-01-18 20:43:47 +01001513 int result = EBADF;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001514
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001515retry:
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001516 sfd = connect_usbmuxd_socket();
1517 if (sfd < 0) {
Nikias Bassenc75605d2019-01-18 20:43:47 +01001518 result = errno;
1519 LIBUSBMUXD_DEBUG(1, "%s: Error: Connection to usbmuxd failed: %s\n", __func__, strerror(result));
1520 return -result;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001521 }
1522
Nikias Bassen232d27e2014-01-08 15:16:07 +01001523 tag = ++use_tag;
1524 if (send_connect_packet(sfd, tag, (uint32_t)handle, (uint16_t)port) <= 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001525 LIBUSBMUXD_DEBUG(1, "%s: Error sending connect message!\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001526 } else {
1527 // read ACK
Nikias Bassenc75605d2019-01-18 20:43:47 +01001528 uint32_t res = -1;
Nikias Bassen450c7152018-10-15 01:33:12 +02001529 LIBUSBMUXD_DEBUG(2, "%s: Reading connect result...\n", __func__);
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001530 if (usbmuxd_get_result(sfd, tag, &res, NULL) == 1) {
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001531 if (res == 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001532 LIBUSBMUXD_DEBUG(2, "%s: Connect success!\n", __func__);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001533 connected = 1;
1534 } else {
Nikias Bassen090492d2013-12-11 21:04:53 +01001535 if ((res == RESULT_BADVERSION) && (proto_version == 1)) {
1536 proto_version = 0;
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001537 socket_close(sfd);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001538 goto retry;
1539 }
Nikias Bassen450c7152018-10-15 01:33:12 +02001540 LIBUSBMUXD_DEBUG(1, "%s: Connect failed, Error code=%d\n", __func__, res);
Nikias Bassenc75605d2019-01-18 20:43:47 +01001541 if (res == RESULT_CONNREFUSED) {
1542 result = ECONNREFUSED;
1543 } else if (res == RESULT_BADDEV) {
1544 result = ENODEV;
1545 } else {
1546 result = EBADF;
1547 }
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001548 }
1549 }
1550 }
1551
1552 if (connected) {
1553 return sfd;
1554 }
1555
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001556 socket_close(sfd);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001557
Nikias Bassenc75605d2019-01-18 20:43:47 +01001558 return -result;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001559}
1560
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001561USBMUXD_API int usbmuxd_disconnect(int sfd)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001562{
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001563 return socket_close(sfd);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001564}
1565
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001566USBMUXD_API int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001567{
1568 int num_sent;
1569
1570 if (sfd < 0) {
1571 return -EINVAL;
1572 }
Martin Szulecki4a6182b2020-06-08 22:53:58 +02001573
Nikias Bassen6603bbc2014-03-10 17:01:57 +01001574 num_sent = socket_send(sfd, (void*)data, len);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001575 if (num_sent < 0) {
1576 *sent_bytes = 0;
Nikias Bassen5005c852014-01-10 20:07:46 +01001577 num_sent = errno;
Nikias Bassen450c7152018-10-15 01:33:12 +02001578 LIBUSBMUXD_DEBUG(1, "%s: Error %d when sending: %s\n", __func__, num_sent, strerror(num_sent));
Nikias Bassen2d27e762014-03-11 10:21:14 +01001579 return -num_sent;
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001580 } else if ((uint32_t)num_sent < len) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001581 LIBUSBMUXD_DEBUG(1, "%s: Warning: Did not send enough (only %d of %d)\n", __func__, num_sent, len);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001582 }
1583
1584 *sent_bytes = num_sent;
1585
1586 return 0;
1587}
1588
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001589USBMUXD_API int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001590{
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001591 int num_recv = socket_receive_timeout(sfd, (void*)data, len, 0, timeout);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001592 if (num_recv < 0) {
1593 *recv_bytes = 0;
1594 return num_recv;
1595 }
1596
1597 *recv_bytes = num_recv;
1598
1599 return 0;
1600}
1601
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001602USBMUXD_API int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001603{
1604 return usbmuxd_recv_timeout(sfd, data, len, recv_bytes, 5000);
1605}
1606
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001607USBMUXD_API int usbmuxd_read_buid(char **buid)
Nikias Bassen75534a52013-12-13 11:59:32 +01001608{
1609 int sfd;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001610 int tag;
Nikias Bassene5fbc1d2014-09-11 12:03:14 +02001611 int ret = -1;
Nikias Bassen75534a52013-12-13 11:59:32 +01001612
1613 if (!buid) {
1614 return -EINVAL;
1615 }
1616 *buid = NULL;
1617
1618 sfd = connect_usbmuxd_socket();
1619 if (sfd < 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001620 LIBUSBMUXD_DEBUG(1, "%s: Error: Connection to usbmuxd failed: %s\n", __func__, strerror(errno));
Nikias Bassen75534a52013-12-13 11:59:32 +01001621 return sfd;
1622 }
1623
1624 proto_version = 1;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001625 tag = ++use_tag;
1626 if (send_read_buid_packet(sfd, tag) <= 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001627 LIBUSBMUXD_DEBUG(1, "%s: Error sending ReadBUID message!\n", __func__);
Nikias Bassen75534a52013-12-13 11:59:32 +01001628 } else {
1629 uint32_t rc = 0;
1630 plist_t pl = NULL;
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001631 ret = usbmuxd_get_result(sfd, tag, &rc, &pl);
1632 if ((ret == 1) && (rc == 0)) {
Nikias Bassen75534a52013-12-13 11:59:32 +01001633 plist_t node = plist_dict_get_item(pl, "BUID");
1634 if (node && plist_get_node_type(node) == PLIST_STRING) {
1635 plist_get_string_val(node, buid);
1636 }
Nikias Bassene5fbc1d2014-09-11 12:03:14 +02001637 ret = 0;
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001638 } else if (ret == 1) {
Nikias Bassen75534a52013-12-13 11:59:32 +01001639 ret = -(int)rc;
1640 }
Nikias Bassend2eb6802014-03-02 03:32:54 +01001641 plist_free(pl);
Nikias Bassen75534a52013-12-13 11:59:32 +01001642 }
Nikias Bassen6340b352014-04-24 21:06:09 +02001643 socket_close(sfd);
Nikias Bassen75534a52013-12-13 11:59:32 +01001644
1645 return ret;
1646}
1647
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001648USBMUXD_API int usbmuxd_read_pair_record(const char* record_id, char **record_data, uint32_t *record_size)
Nikias Bassen30b64de2013-12-13 12:01:45 +01001649{
1650 int sfd;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001651 int tag;
Nikias Bassen30b64de2013-12-13 12:01:45 +01001652 int ret = -1;
1653
1654 if (!record_id || !record_data || !record_size) {
1655 return -EINVAL;
1656 }
1657 *record_data = NULL;
1658 *record_size = 0;
1659
1660 sfd = connect_usbmuxd_socket();
1661 if (sfd < 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001662 LIBUSBMUXD_DEBUG(1, "%s: Error: Connection to usbmuxd failed: %s\n",
Nikias Bassen30b64de2013-12-13 12:01:45 +01001663 __func__, strerror(errno));
1664 return sfd;
1665 }
1666
1667 proto_version = 1;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001668 tag = ++use_tag;
Nikias Bassen30b64de2013-12-13 12:01:45 +01001669
Nikias Bassen07a493a2018-09-15 03:44:58 +02001670 if (send_pair_record_packet(sfd, tag, "ReadPairRecord", record_id, 0, NULL) <= 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001671 LIBUSBMUXD_DEBUG(1, "%s: Error sending ReadPairRecord message!\n", __func__);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001672 } else {
1673 uint32_t rc = 0;
1674 plist_t pl = NULL;
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001675 ret = usbmuxd_get_result(sfd, tag, &rc, &pl);
1676 if ((ret == 1) && (rc == 0)) {
Nikias Bassen30b64de2013-12-13 12:01:45 +01001677 plist_t node = plist_dict_get_item(pl, "PairRecordData");
1678 if (node && plist_get_node_type(node) == PLIST_DATA) {
1679 uint64_t int64val = 0;
1680 plist_get_data_val(node, record_data, &int64val);
Aaron Burghardt36d522c2014-06-12 14:24:53 -04001681 if (*record_data && int64val > 0) {
Nikias Bassen30b64de2013-12-13 12:01:45 +01001682 *record_size = (uint32_t)int64val;
1683 ret = 0;
1684 }
1685 }
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001686 } else if (ret == 1) {
Nikias Bassen30b64de2013-12-13 12:01:45 +01001687 ret = -(int)rc;
1688 }
Nikias Bassend2eb6802014-03-02 03:32:54 +01001689 plist_free(pl);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001690 }
Nikias Bassen6340b352014-04-24 21:06:09 +02001691 socket_close(sfd);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001692
1693 return ret;
1694}
1695
Nikias Bassen07a493a2018-09-15 03:44:58 +02001696USBMUXD_API int usbmuxd_save_pair_record_with_device_id(const char* record_id, uint32_t device_id, const char *record_data, uint32_t record_size)
Nikias Bassen30b64de2013-12-13 12:01:45 +01001697{
1698 int sfd;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001699 int tag;
Nikias Bassen30b64de2013-12-13 12:01:45 +01001700 int ret = -1;
1701
1702 if (!record_id || !record_data || !record_size) {
1703 return -EINVAL;
1704 }
1705
1706 sfd = connect_usbmuxd_socket();
1707 if (sfd < 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001708 LIBUSBMUXD_DEBUG(1, "%s: Error: Connection to usbmuxd failed: %s\n",
Nikias Bassen30b64de2013-12-13 12:01:45 +01001709 __func__, strerror(errno));
1710 return sfd;
1711 }
1712
1713 proto_version = 1;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001714 tag = ++use_tag;
Nikias Bassen30b64de2013-12-13 12:01:45 +01001715
1716 plist_t data = plist_new_data(record_data, record_size);
Nikias Bassen07a493a2018-09-15 03:44:58 +02001717 if (send_pair_record_packet(sfd, tag, "SavePairRecord", record_id, device_id, data) <= 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001718 LIBUSBMUXD_DEBUG(1, "%s: Error sending SavePairRecord message!\n", __func__);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001719 } else {
1720 uint32_t rc = 0;
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001721 ret = usbmuxd_get_result(sfd, tag, &rc, NULL);
1722 if ((ret == 1) && (rc == 0)) {
Nikias Bassen30b64de2013-12-13 12:01:45 +01001723 ret = 0;
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001724 } else if (ret == 1) {
Nikias Bassen30b64de2013-12-13 12:01:45 +01001725 ret = -(int)rc;
Nikias Bassen450c7152018-10-15 01:33:12 +02001726 LIBUSBMUXD_DEBUG(1, "%s: Error: saving pair record failed: %d\n", __func__, ret);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001727 }
1728 }
1729 plist_free(data);
Nikias Bassen6340b352014-04-24 21:06:09 +02001730 socket_close(sfd);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001731
1732 return ret;
1733}
1734
Nikias Bassen07a493a2018-09-15 03:44:58 +02001735USBMUXD_API int usbmuxd_save_pair_record(const char* record_id, const char *record_data, uint32_t record_size)
1736{
1737 return usbmuxd_save_pair_record_with_device_id(record_id, 0, record_data, record_size);
1738}
1739
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001740USBMUXD_API int usbmuxd_delete_pair_record(const char* record_id)
Nikias Bassen30b64de2013-12-13 12:01:45 +01001741{
1742 int sfd;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001743 int tag;
Nikias Bassen30b64de2013-12-13 12:01:45 +01001744 int ret = -1;
1745
1746 if (!record_id) {
1747 return -EINVAL;
1748 }
1749
1750 sfd = connect_usbmuxd_socket();
1751 if (sfd < 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001752 LIBUSBMUXD_DEBUG(1, "%s: Error: Connection to usbmuxd failed: %s\n",
Nikias Bassen30b64de2013-12-13 12:01:45 +01001753 __func__, strerror(errno));
1754 return sfd;
1755 }
1756
1757 proto_version = 1;
Nikias Bassen232d27e2014-01-08 15:16:07 +01001758 tag = ++use_tag;
Nikias Bassen30b64de2013-12-13 12:01:45 +01001759
Nikias Bassen07a493a2018-09-15 03:44:58 +02001760 if (send_pair_record_packet(sfd, tag, "DeletePairRecord", record_id, 0, NULL) <= 0) {
Nikias Bassen450c7152018-10-15 01:33:12 +02001761 LIBUSBMUXD_DEBUG(1, "%s: Error sending DeletePairRecord message!\n", __func__);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001762 } else {
1763 uint32_t rc = 0;
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001764 ret = usbmuxd_get_result(sfd, tag, &rc, NULL);
1765 if ((ret == 1) && (rc == 0)) {
Nikias Bassen30b64de2013-12-13 12:01:45 +01001766 ret = 0;
Nikias Bassen4e4dec22014-03-24 03:07:19 +01001767 } else if (ret == 1) {
Nikias Bassen30b64de2013-12-13 12:01:45 +01001768 ret = -(int)rc;
Nikias Bassen450c7152018-10-15 01:33:12 +02001769 LIBUSBMUXD_DEBUG(1, "%s: Error: deleting pair record failed: %d\n", __func__, ret);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001770 }
1771 }
Nikias Bassen6340b352014-04-24 21:06:09 +02001772 socket_close(sfd);
Nikias Bassen30b64de2013-12-13 12:01:45 +01001773
1774 return ret;
1775}
1776
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001777USBMUXD_API void libusbmuxd_set_use_inotify(int set)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001778{
1779#ifdef HAVE_INOTIFY
1780 use_inotify = set;
1781#endif
1782 return;
1783}
1784
Chow Loong Jinc0b6aed2014-10-03 13:31:48 +02001785USBMUXD_API void libusbmuxd_set_debug_level(int level)
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001786{
1787 libusbmuxd_debug = level;
Martin Szulecki9cc128a2014-02-10 21:22:59 +01001788 socket_set_verbose(level);
Nikias Bassenc45ae1f2013-09-17 11:00:31 +02001789}