From c45ae1f6b6f53995a5bc99591688102d11ad2148 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Tue, 17 Sep 2013 11:00:31 +0200 Subject: initial commit of adapted source tree. --- src/libusbmuxd.c | 980 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 980 insertions(+) create mode 100644 src/libusbmuxd.c (limited to 'src/libusbmuxd.c') 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 +Copyright (C) 2009 Paul Sladen +Copyright (C) 2009 Martin Szulecki + +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 +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef WIN32 +#include +#include +#define sleep(x) Sleep(x*1000) +#ifndef EPROTO +#define EPROTO 134 +#endif +#ifndef EBADMSG +#define EBADMSG 104 +#endif +#else +#include +#include +#include +#endif + +#ifdef HAVE_INOTIFY +#include +#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 +#include + +#ifdef HAVE_PLIST +#include +#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); +} -- cgit v1.1-32-gdbae