diff options
Diffstat (limited to 'src/libusbmuxd.c')
| -rw-r--r-- | src/libusbmuxd.c | 980 | 
1 files changed, 980 insertions, 0 deletions
| diff --git a/src/libusbmuxd.c b/src/libusbmuxd.c new file mode 100644 index 0000000..64b3725 --- /dev/null +++ b/src/libusbmuxd.c @@ -0,0 +1,980 @@ +/* +	libusbmuxd - client library to talk to usbmuxd + +Copyright (C) 2009-2010	Nikias Bassen <nikias@gmx.li> +Copyright (C) 2009	Paul Sladen <libiphone@paul.sladen.org> +Copyright (C) 2009	Martin Szulecki <opensuse@sukimashita.com> + +This library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 2.1 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + +*/ + +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WIN32 +#include <windows.h> +#include <winsock2.h> +#define sleep(x) Sleep(x*1000) +#ifndef EPROTO +#define EPROTO 134 +#endif +#ifndef EBADMSG +#define EBADMSG 104 +#endif +#else +#include <sys/socket.h> +#include <arpa/inet.h> +#include <pthread.h> +#endif + +#ifdef HAVE_INOTIFY +#include <sys/inotify.h> +#define EVENT_SIZE  (sizeof (struct inotify_event)) +#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16)) +#define USBMUXD_DIRNAME "/var/run" +#define USBMUXD_SOCKET_NAME "usbmuxd" +#endif /* HAVE_INOTIFY */ + +#include <unistd.h> +#include <signal.h> + +#ifdef HAVE_PLIST +#include <plist/plist.h> +#define PLIST_BUNDLE_ID "com.marcansoft.usbmuxd" +#define PLIST_CLIENT_VERSION_STRING "usbmuxd built for freedom" +#define PLIST_PROGNAME "libusbmuxd" +#endif + +// usbmuxd public interface +#include "usbmuxd.h" +// usbmuxd protocol +#include "usbmuxd-proto.h" +// socket utility functions +#include "sock_stuff.h" +// misc utility functions +#include "collection.h" + +static int libusbmuxd_debug = 2; +#define DEBUG(x, y, ...) if (x <= libusbmuxd_debug) fprintf(stderr, (y), __VA_ARGS__); + +static struct collection devices; +static usbmuxd_event_cb_t event_cb = NULL; +#ifdef WIN32 +HANDLE devmon = NULL; +CRITICAL_SECTION mutex; +static int mutex_initialized = 0; +#define LOCK if (!mutex_initialized) { InitializeCriticalSection(&mutex); mutex_initialized = 1; } EnterCriticalSection(&mutex); +#define UNLOCK LeaveCriticalSection(&mutex); +#else +pthread_t devmon; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +#define LOCK pthread_mutex_lock(&mutex) +#define UNLOCK pthread_mutex_unlock(&mutex)	 +#endif +static int listenfd = -1; + +static int use_tag = 0; +static int proto_version = 0; + +/** + * Finds a device info record by its handle. + * if the record is not found, NULL is returned. + */ +static usbmuxd_device_info_t *devices_find(uint32_t handle) +{ +	FOREACH(usbmuxd_device_info_t *dev, &devices) { +		if (dev && dev->handle == handle) { +			return dev; +		} +	} ENDFOREACH +	return NULL; +} + +/** + * Creates a socket connection to usbmuxd. + * For Mac/Linux it is a unix domain socket, + * for Windows it is a tcp socket. + */ +static int connect_usbmuxd_socket() +{ +#if defined(WIN32) || defined(__CYGWIN__) +	return connect_socket("127.0.0.1", USBMUXD_SOCKET_PORT); +#else +	return connect_unix_socket(USBMUXD_SOCKET_FILE); +#endif +} + +static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload, int timeout) +{ +	int recv_len; +	struct usbmuxd_header hdr; +	char *payload_loc = NULL; + +	header->length = 0; +	header->version = 0; +	header->message = 0; +	header->tag = 0; + +	recv_len = recv_buf_timeout(sfd, &hdr, sizeof(hdr), 0, timeout); +	if (recv_len < 0) { +		return recv_len; +	} else if ((size_t)recv_len < sizeof(hdr)) { +		return recv_len; +	} + +	uint32_t payload_size = hdr.length - sizeof(hdr); +	if (payload_size > 0) { +		payload_loc = (char*)malloc(payload_size); +		if (recv_buf_timeout(sfd, payload_loc, payload_size, 0, 5000) != (int)payload_size) { +			DEBUG(1, "%s: Error receiving payload of size %d\n", __func__, payload_size); +			free(payload_loc); +			return -EBADMSG; +		} +	} + +#ifdef HAVE_PLIST +	if (hdr.message == MESSAGE_PLIST) { +		char *message = NULL; +		plist_t plist = NULL; +		plist_from_xml(payload_loc, payload_size, &plist); +		free(payload_loc); + +		if (!plist) { +			DEBUG(1, "%s: Error getting plist from payload!\n", __func__); +			return -EBADMSG; +		} + +		plist_t node = plist_dict_get_item(plist, "MessageType"); +		if (plist_get_node_type(node) != PLIST_STRING) { +			DEBUG(1, "%s: Error getting message type from plist!\n", __func__); +			free(plist); +			return -EBADMSG; +		} +		 +		plist_get_string_val(node, &message); +		if (message) { +			uint64_t val = 0; +			if (strcmp(message, "Result") == 0) { +				/* result message */ +				uint32_t dwval = 0; +				plist_t n = plist_dict_get_item(plist, "Number"); +				plist_get_uint_val(n, &val); +				*payload = malloc(sizeof(uint32_t)); +				dwval = val; +				memcpy(*payload, &dwval, sizeof(dwval)); +				hdr.length = sizeof(hdr) + sizeof(dwval); +				hdr.message = MESSAGE_RESULT; +			} else if (strcmp(message, "Attached") == 0) { +				/* device add message */ +				struct usbmuxd_device_record *dev = NULL; +				plist_t props = plist_dict_get_item(plist, "Properties"); +				if (!props) { +					DEBUG(1, "%s: Could not get properties for message '%s' from plist!\n", __func__, message); +					free(message); +					plist_free(plist); +					return -EBADMSG; +				} +				dev = (struct usbmuxd_device_record*)malloc(sizeof(struct usbmuxd_device_record)); +				memset(dev, 0, sizeof(struct usbmuxd_device_record)); + +				plist_t n = plist_dict_get_item(props, "DeviceID"); +				plist_get_uint_val(n, &val); +				dev->device_id = (uint32_t)val; + +				n = plist_dict_get_item(props, "ProductID"); +				plist_get_uint_val(n, &val); +				dev->product_id = (uint32_t)val; + +				n = plist_dict_get_item(props, "SerialNumber"); +				char *strval = NULL; +				plist_get_string_val(n, &strval); +				if (strval) { +					strncpy(dev->serial_number, strval, 255); +					free(strval); +				} +				n = plist_dict_get_item(props, "LocationID"); +				plist_get_uint_val(n, &val); +				dev->location = (uint32_t)val; +				*payload = (void*)dev; +				hdr.length = sizeof(hdr) + sizeof(struct usbmuxd_device_record); +				hdr.message = MESSAGE_DEVICE_ADD; +			} else if (strcmp(message, "Detached") == 0) { +				/* device remove message */ +				uint32_t dwval = 0; +				plist_t n = plist_dict_get_item(plist, "DeviceID"); +				if (n) { +					plist_get_uint_val(n, &val); +					*payload = malloc(sizeof(uint32_t)); +					dwval = val; +					memcpy(*payload, &dwval, sizeof(dwval)); +					hdr.length = sizeof(hdr) + sizeof(dwval); +					hdr.message = MESSAGE_DEVICE_REMOVE; +				} +			} else { +				DEBUG(1, "%s: Unexpected message '%s' in plist!\n", __func__, message); +				free(message); +				plist_free(plist); +				return -EBADMSG; +			} +			free(message); +		} +		plist_free(plist); +	} else +#endif +	{ +		*payload = payload_loc; +	} + +	memcpy(header, &hdr, sizeof(hdr)); + +	return hdr.length; +} + +/** + * Retrieves the result code to a previously sent request. + */ +static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result) +{ +	struct usbmuxd_header hdr; +	int recv_len; +	uint32_t *res = NULL; + +	if (!result) { +		return -EINVAL; +	} +	*result = -1; + +	if ((recv_len = receive_packet(sfd, &hdr, (void**)&res, 5000)) < 0) { +		DEBUG(1, "%s: Error receiving packet: %d\n", __func__, errno); +		if (res) +			free(res); +		return -errno; +	} +	if ((size_t)recv_len < sizeof(hdr)) { +		DEBUG(1, "%s: Received packet is too small!\n", __func__); +		if (res) +			free(res); +		return -EPROTO; +	} + +	if (hdr.message == MESSAGE_RESULT) { +		int ret = 0; +		if (res && (hdr.tag == tag)) { +			memcpy(result, res, sizeof(uint32_t)); +			ret = 1; +		} +		if (res) +			free(res); +		return ret; +	} +	DEBUG(1, "%s: Unexpected message of type %d received!\n", __func__, hdr.message); +	if (res) +		free(res); +	return -EPROTO; +} + +static int send_packet(int sfd, uint32_t message, uint32_t tag, void *payload, uint32_t payload_size) +{ +	struct usbmuxd_header header; + +	header.length = sizeof(struct usbmuxd_header); +	header.version = proto_version; +	header.message = message; +	header.tag = tag; +	if (payload && (payload_size > 0)) { +		header.length += payload_size; +	} +	int sent = send_buf(sfd, &header, sizeof(header)); +	if (sent != sizeof(header)) { +		DEBUG(1, "%s: ERROR: could not send packet header\n", __func__); +		return -1; +	} +	if (payload && (payload_size > 0)) { +		sent += send_buf(sfd, payload, payload_size); +	} +	if (sent != (int)header.length) { +		DEBUG(1, "%s: ERROR: could not send whole packet\n", __func__); +		close_socket(sfd); +		return -1; +	} +	return sent; +} + +static int send_listen_packet(int sfd, uint32_t tag) +{ +	int res = 0; +#ifdef HAVE_PLIST +	if (proto_version == 1) { +		/* plist packet */ +		char *payload = NULL; +		uint32_t payload_size = 0; +		plist_t plist; + +		/* construct message plist */ +		plist = plist_new_dict(); +		plist_dict_insert_item(plist, "BundleID", plist_new_string(PLIST_BUNDLE_ID)); +		plist_dict_insert_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING)); +		plist_dict_insert_item(plist, "MessageType", plist_new_string("Listen")); +		plist_dict_insert_item(plist, "ProgName", plist_new_string(PLIST_PROGNAME)); +		plist_to_xml(plist, &payload, &payload_size); +		plist_free(plist); + +		res = send_packet(sfd, MESSAGE_PLIST, tag, payload, payload_size); +		free(payload); +	} else +#endif +	{ +		/* binary packet */ +		res = send_packet(sfd, MESSAGE_LISTEN, tag, NULL, 0); +	} +	return res; +} + +static int send_connect_packet(int sfd, uint32_t tag, uint32_t device_id, uint16_t port) +{ +	int res = 0; +#ifdef HAVE_PLIST +	if (proto_version == 1) { +		/* plist packet */ +		char *payload = NULL; +		uint32_t payload_size = 0; +		plist_t plist; + +		/* construct message plist */ +		plist = plist_new_dict(); +		plist_dict_insert_item(plist, "BundleID", plist_new_string(PLIST_BUNDLE_ID)); +		plist_dict_insert_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING)); +		plist_dict_insert_item(plist, "MessageType", plist_new_string("Connect")); +		plist_dict_insert_item(plist, "DeviceID", plist_new_uint(device_id)); +		plist_dict_insert_item(plist, "PortNumber", plist_new_uint(htons(port))); +		plist_dict_insert_item(plist, "ProgName", plist_new_string(PLIST_PROGNAME)); +		plist_to_xml(plist, &payload, &payload_size); +		plist_free(plist); + +		res = send_packet(sfd, MESSAGE_PLIST, tag, (void*)payload, payload_size); +		free(payload); +	} else +#endif +	{ +		/* binary packet */ +		struct { +			uint32_t device_id; +			uint16_t port; +			uint16_t reserved; +		} conninfo; + +		conninfo.device_id = device_id; +		conninfo.port = htons(port); +		conninfo.reserved = 0; + +		res = send_packet(sfd, MESSAGE_CONNECT, tag, &conninfo, sizeof(conninfo)); +	} +	return res; +} + +/** + * Generates an event, i.e. calls the callback function. + * A reference to a populated usbmuxd_event_t with information about the event + * and the corresponding device will be passed to the callback function. + */ +static void generate_event(usbmuxd_event_cb_t callback, const usbmuxd_device_info_t *dev, enum usbmuxd_event_type event, void *user_data) +{ +	usbmuxd_event_t ev; + +	if (!callback || !dev) { +		return; +	} + +	ev.event = event; +	memcpy(&ev.device, dev, sizeof(usbmuxd_device_info_t)); + +	callback(&ev, user_data); +} + +static int usbmuxd_listen_poll() +{ +	int sfd; + +	sfd = connect_usbmuxd_socket(); +	if (sfd < 0) { +		while (event_cb) { +			if ((sfd = connect_usbmuxd_socket()) > 0) { +				break; +			} +			sleep(1); +		} +	} + +	return sfd; +} + +#ifdef HAVE_INOTIFY +static int use_inotify = 1; + +static int usbmuxd_listen_inotify() +{ +	int inot_fd; +	int watch_d; +	int sfd; + +	if (!use_inotify) { +		return -2; +	} + +	sfd = connect_usbmuxd_socket(); +	if (sfd >= 0) +		return sfd; + +	sfd = -1; +	inot_fd = inotify_init (); +	if (inot_fd < 0) { +		DEBUG(1, "%s: Failed to setup inotify\n", __func__); +		return -2; +	} + +	/* inotify is setup, listen for events that concern us */ +	watch_d = inotify_add_watch (inot_fd, USBMUXD_DIRNAME, IN_CREATE); +	if (watch_d < 0) { +		DEBUG(1, "%s: Failed to setup watch descriptor for socket dir\n", __func__); +		close (inot_fd); +		return -2; +	} + +	while (1) { +		ssize_t len, i; +		char buff[EVENT_BUF_LEN] = {0}; + +		i = 0; +		len = read (inot_fd, buff, EVENT_BUF_LEN -1); +		if (len < 0) +			goto end; +		while (i < len) { +			struct inotify_event *pevent = (struct inotify_event *) & buff[i]; + +			/* check that it's ours */ +			if (pevent->mask & IN_CREATE && +			    pevent->len && +			    pevent->name != NULL && +			    strcmp(pevent->name, USBMUXD_SOCKET_NAME) == 0) { +				sfd = connect_usbmuxd_socket (); +				goto end; +			} +			i += EVENT_SIZE + pevent->len; +		} +	} + +end: +	inotify_rm_watch(inot_fd, watch_d); +	close(inot_fd); + +	return sfd; +} +#endif /* HAVE_INOTIFY */ + +/** + * Tries to connect to usbmuxd and wait if it is not running. + */ +static int usbmuxd_listen() +{ +	int sfd; +	uint32_t res = -1; + +#ifdef HAVE_PLIST +retry: +#endif + +#ifdef HAVE_INOTIFY +	sfd = usbmuxd_listen_inotify(); +	if (sfd == -2) +		sfd = usbmuxd_listen_poll(); +#else +	sfd = usbmuxd_listen_poll(); +#endif + +	if (sfd < 0) { +		DEBUG(1, "%s: ERROR: usbmuxd was supposed to be running here...\n", __func__); +		return sfd; +	} + +	use_tag++; +	LOCK; +	if (send_listen_packet(sfd, use_tag) <= 0) { +		UNLOCK; +		DEBUG(1, "%s: ERROR: could not send listen packet\n", __func__); +		close_socket(sfd); +		return -1; +	} +	if (usbmuxd_get_result(sfd, use_tag, &res) && (res != 0)) { +		UNLOCK; +		close_socket(sfd); +#ifdef HAVE_PLIST +		if ((res == RESULT_BADVERSION) && (proto_version != 1)) { +			proto_version = 1; +			goto retry; +		} +#endif +		DEBUG(1, "%s: ERROR: did not get OK but %d\n", __func__, res); +		return -1; +	} +	UNLOCK; + +	return sfd; +} + +/** + * Waits for an event to occur, i.e. a packet coming from usbmuxd. + * Calls generate_event to pass the event via callback to the client program. + */ +static int get_next_event(int sfd, usbmuxd_event_cb_t callback, void *user_data) +{ +	struct usbmuxd_header hdr; +	void *payload = NULL; + +	/* block until we receive something */ +	if (receive_packet(sfd, &hdr, &payload, 0) < 0) { +		// when then usbmuxd connection fails, +		// generate remove events for every device that +		// is still present so applications know about it +		FOREACH(usbmuxd_device_info_t *dev, &devices) { +			generate_event(callback, dev, UE_DEVICE_REMOVE, user_data); +			collection_remove(&devices, dev); +			free(dev); +		} ENDFOREACH +		return -EIO; +	} + +	if ((hdr.length > sizeof(hdr)) && !payload) { +		DEBUG(1, "%s: Invalid packet received, payload is missing!\n", __func__); +		return -EBADMSG; +	} + +	if (hdr.message == MESSAGE_DEVICE_ADD) { +		struct usbmuxd_device_record *dev = payload; +		usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t)); +		if (!devinfo) { +			DEBUG(1, "%s: Out of memory!\n", __func__); +			free(payload); +			return -1; +		} + +		devinfo->handle = dev->device_id; +		devinfo->product_id = dev->product_id; +		memset(devinfo->udid, '\0', sizeof(devinfo->udid)); +		memcpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid)); + +		if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) { +			sprintf(devinfo->udid + 32, "%08x", devinfo->handle); +		} + +		collection_add(&devices, devinfo); +		generate_event(callback, devinfo, UE_DEVICE_ADD, user_data); +	} else if (hdr.message == MESSAGE_DEVICE_REMOVE) { +		uint32_t handle; +		usbmuxd_device_info_t *devinfo; + +		memcpy(&handle, payload, sizeof(uint32_t)); + +		devinfo = devices_find(handle); +		if (!devinfo) { +			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); +		} else { +			generate_event(callback, devinfo, UE_DEVICE_REMOVE, user_data); +			collection_remove(&devices, devinfo); +			free(devinfo); +		} +	} else if (hdr.length > 0) { +		DEBUG(1, "%s: Unexpected message type %d length %d received!\n", __func__, hdr.message, hdr.length); +	} +	if (payload) { +		free(payload); +	} +	return 0; +} + +static void device_monitor_cleanup(void* data) +{ +	FOREACH(usbmuxd_device_info_t *dev, &devices) { +		collection_remove(&devices, dev); +		free(dev); +	} ENDFOREACH +	collection_free(&devices); + +	close_socket(listenfd); +	listenfd = -1; +} + +/** + * Device Monitor thread function. + * + * This function sets up a connection to usbmuxd + */ +static void *device_monitor(void *data) +{ +	collection_init(&devices); + +#ifndef WIN32 +	pthread_cleanup_push(device_monitor_cleanup, NULL); +#endif +	while (event_cb) { + +		listenfd = usbmuxd_listen(); +		if (listenfd < 0) { +			continue; +		} + +		while (event_cb) { +			int res = get_next_event(listenfd, event_cb, data); +			if (res < 0) { +			    break; +			} +		} +	} + +#ifndef WIN32 +	pthread_cleanup_pop(1); +#else +	device_monitor_cleanup(NULL); +#endif +	return NULL; +} + +int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data) +{ +	int res; + +	if (!callback) { +		return -EINVAL; +	} +	event_cb = callback; + +#ifdef WIN32 +	res = 0; +	devmon = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)device_monitor, user_data, 0, NULL); +	if (devmon == NULL) { +		res = GetLastError(); +	} +#else +	res = pthread_create(&devmon, NULL, device_monitor, user_data); +#endif +	if (res != 0) { +		DEBUG(1, "%s: ERROR: Could not start device watcher thread!\n", __func__); +		return res; +	} +	return 0; +} + +int usbmuxd_unsubscribe() +{ +	event_cb = NULL; + +	shutdown_socket(listenfd, SHUT_RDWR); + +#ifdef WIN32 +	if (devmon != NULL) { +		WaitForSingleObject(devmon, INFINITE); +	} +#else +	if (pthread_kill(devmon, 0) == 0) { +		pthread_cancel(devmon); +		pthread_join(devmon, NULL); +	} +#endif + +	return 0; +} + +int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list) +{ +	int sfd; +	int listen_success = 0; +	uint32_t res; +	struct collection tmpdevs; +	usbmuxd_device_info_t *newlist = NULL; +	struct usbmuxd_header hdr; +	struct usbmuxd_device_record *dev; +	int dev_cnt = 0; +	void *payload = NULL; + +	*device_list = NULL; + +#ifdef HAVE_PLIST +retry: +#endif +	sfd = connect_usbmuxd_socket(); +	if (sfd < 0) { +		DEBUG(1, "%s: error opening socket!\n", __func__); +		return sfd; +	} + +	use_tag++; +	LOCK; +	if (send_listen_packet(sfd, use_tag) > 0) { +		res = -1; +		// get response +		if (usbmuxd_get_result(sfd, use_tag, &res) && (res == 0)) { +			listen_success = 1; +		} else { +			UNLOCK; +			close_socket(sfd); +#ifdef HAVE_PLIST +			if ((res == RESULT_BADVERSION) && (proto_version != 1)) { +				proto_version = 1; +				goto retry; +			} +#endif +			DEBUG(1, "%s: Did not get response to scan request (with result=0)...\n", __func__); +			return res; +		} +	} + +	if (!listen_success) { +		UNLOCK; +		DEBUG(1, "%s: Could not send listen request!\n", __func__); +		return -1; +	} + +	collection_init(&tmpdevs); + +	// receive device list +	while (1) { +		if (receive_packet(sfd, &hdr, &payload, 1000) > 0) { +			if (hdr.message == MESSAGE_DEVICE_ADD) { +				dev = payload; +				usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t)); +				if (!devinfo) { +					UNLOCK; +					DEBUG(1, "%s: Out of memory!\n", __func__); +					free(payload); +					return -1; +				} + +				devinfo->handle = dev->device_id; +				devinfo->product_id = dev->product_id; +				memset(devinfo->udid, '\0', sizeof(devinfo->udid)); +				memcpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid)); + +				if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) { +					sprintf(devinfo->udid + 32, "%08x", devinfo->handle); +				} + +				collection_add(&tmpdevs, devinfo); + +			} else if (hdr.message == MESSAGE_DEVICE_REMOVE) { +				uint32_t handle; +				usbmuxd_device_info_t *devinfo = NULL; + +				memcpy(&handle, payload, sizeof(uint32_t)); + +				FOREACH(usbmuxd_device_info_t *di, &tmpdevs) { +					if (di && di->handle == handle) { +						devinfo = di; +						break; +					} +				} ENDFOREACH +				if (devinfo) { +					collection_remove(&tmpdevs, devinfo); +					free(devinfo); +				} +			} else { +				DEBUG(1, "%s: Unexpected message %d\n", __func__, hdr.message); +			} +			if (payload) +				free(payload); +		} else { +			// we _should_ have all of them now. +			// or perhaps an error occured. +			break; +		} +	} +	UNLOCK; + +	// explicitly close connection +	close_socket(sfd); + +	// create copy of device info entries from collection +	newlist = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t) * (collection_count(&tmpdevs) + 1)); +	dev_cnt = 0; +	FOREACH(usbmuxd_device_info_t *di, &tmpdevs) { +		if (di) { +			memcpy(&newlist[dev_cnt], di, sizeof(usbmuxd_device_info_t)); +			free(di); +			dev_cnt++; +		} +	} ENDFOREACH +	collection_free(&tmpdevs); + +	memset(&newlist[dev_cnt], 0, sizeof(usbmuxd_device_info_t)); +	*device_list = newlist; + +	return dev_cnt; +} + +int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list) +{ +	if (device_list) { +		free(*device_list); +	} +	return 0; +} + +int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device) +{ +	usbmuxd_device_info_t *dev_list = NULL; + +	if (!device) { +		return -EINVAL; +	} +	if (usbmuxd_get_device_list(&dev_list) < 0) { +		return -ENODEV; +	} + +	int i; +	int result = 0; +	for (i = 0; dev_list[i].handle > 0; i++) { +	 	if (!udid) { +			device->handle = dev_list[i].handle; +			device->product_id = dev_list[i].product_id; +			strcpy(device->udid, dev_list[i].udid); +			result = 1; +			break; +		} +		if (!strcmp(udid, dev_list[i].udid)) { +			device->handle = dev_list[i].handle; +			device->product_id = dev_list[i].product_id; +			strcpy(device->udid, dev_list[i].udid); +			result = 1; +			break; +		} +	} + +	free(dev_list); + +	return result; +} + +int usbmuxd_connect(const int handle, const unsigned short port) +{ +	int sfd; +	int connected = 0; +	uint32_t res = -1; + +#ifdef HAVE_PLIST +retry: +#endif +	sfd = connect_usbmuxd_socket(); +	if (sfd < 0) { +		DEBUG(1, "%s: Error: Connection to usbmuxd failed: %s\n", +				__func__, strerror(errno)); +		return sfd; +	} + +	use_tag++; +	if (send_connect_packet(sfd, use_tag, (uint32_t)handle, (uint16_t)port) <= 0) { +		DEBUG(1, "%s: Error sending connect message!\n", __func__); +	} else { +		// read ACK +		DEBUG(2, "%s: Reading connect result...\n", __func__); +		if (usbmuxd_get_result(sfd, use_tag, &res)) { +			if (res == 0) { +				DEBUG(2, "%s: Connect success!\n", __func__); +				connected = 1; +			} else { +#ifdef HAVE_PLIST +				if ((res == RESULT_BADVERSION) && (proto_version == 0)) { +					proto_version = 1; +					close_socket(sfd); +					goto retry; +				} +#endif +				DEBUG(1, "%s: Connect failed, Error code=%d\n", __func__, res); +			} +		} +	} + +	if (connected) { +		return sfd; +	} + +	close_socket(sfd); + +	return -1; +} + +int usbmuxd_disconnect(int sfd) +{ +	return close_socket(sfd); +} + +int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes) +{ +	int num_sent; + +	if (sfd < 0) { +		return -EINVAL; +	} +	 +	num_sent = send(sfd, (void*)data, len, 0); +	if (num_sent < 0) { +		*sent_bytes = 0; +		DEBUG(1, "%s: Error %d when sending: %s\n", __func__, num_sent, strerror(errno)); +		return num_sent; +	} else if ((uint32_t)num_sent < len) { +		DEBUG(1, "%s: Warning: Did not send enough (only %d of %d)\n", __func__, num_sent, len); +	} + +	*sent_bytes = num_sent; + +	return 0; +} + +int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) +{ +	int num_recv = recv_buf_timeout(sfd, (void*)data, len, 0, timeout); +	if (num_recv < 0) { +		*recv_bytes = 0; +		return num_recv; +	} + +	*recv_bytes = num_recv; + +	return 0; +} + +int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes) +{ +	return usbmuxd_recv_timeout(sfd, data, len, recv_bytes, 5000); +} + +void libusbmuxd_set_use_inotify(int set) +{ +#ifdef HAVE_INOTIFY +	use_inotify = set; +#endif +	return; +} + +void libusbmuxd_set_debug_level(int level) +{ +	libusbmuxd_debug = level; +	sock_stuff_set_verbose(level); +} | 
