summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2013-09-17 11:00:31 +0200
committerGravatar Nikias Bassen2013-09-17 11:00:31 +0200
commitc45ae1f6b6f53995a5bc99591688102d11ad2148 (patch)
tree03e36986e4ad61f6345c64b7b2f673eebee33816 /src
downloadlibusbmuxd-c45ae1f6b6f53995a5bc99591688102d11ad2148.tar.gz
libusbmuxd-c45ae1f6b6f53995a5bc99591688102d11ad2148.tar.bz2
initial commit of adapted source tree.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am16
-rw-r--r--src/collection.c81
-rw-r--r--src/collection.h48
-rw-r--r--src/libusbmuxd.c980
-rw-r--r--src/sock_stuff.c375
-rw-r--r--src/sock_stuff.h65
6 files changed, 1565 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..bf9198f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,16 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+AM_CFLAGS = $(GLOBAL_CFLAGS) $(libplist_CFLAGS)
+AM_LDFLAGS = $(GLOBAL_LIBS) $(libplist_LIBS)
+
+lib_LTLIBRARIES = libusbmuxd.la
+libusbmuxd_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBUSBMUXD_SO_VERSION) -no-undefined
+libusbmuxd_la_LIBADD =
+libusbmuxd_la_SOURCES = \
+ collection.c collection.h \
+ sock_stuff.c sock_stuff.h \
+ libusbmuxd.c
+
+if WIN32
+libusbmuxd_la_LIBADD += ws2_32
+endif
diff --git a/src/collection.c b/src/collection.c
new file mode 100644
index 0000000..423cc4e
--- /dev/null
+++ b/src/collection.c
@@ -0,0 +1,81 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+
+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
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "collection.h"
+
+void collection_init(struct collection *col)
+{
+ col->list = malloc(sizeof(void *));
+ memset(col->list, 0, sizeof(void *));
+ col->capacity = 1;
+}
+
+void collection_free(struct collection *col)
+{
+ free(col->list);
+ col->list = NULL;
+ col->capacity = 0;
+}
+
+void collection_add(struct collection *col, void *element)
+{
+ int i;
+ for(i=0; i<col->capacity; i++) {
+ if(!col->list[i]) {
+ col->list[i] = element;
+ return;
+ }
+ }
+ col->list = realloc(col->list, sizeof(void*) * col->capacity * 2);
+ memset(&col->list[col->capacity], 0, sizeof(void *) * col->capacity);
+ col->list[col->capacity] = element;
+ col->capacity *= 2;
+}
+
+void collection_remove(struct collection *col, void *element)
+{
+ int i;
+ for(i=0; i<col->capacity; i++) {
+ if(col->list[i] == element) {
+ col->list[i] = NULL;
+ return;
+ }
+ }
+ fprintf(stderr, "%s: WARNING: element %p not present in collection %p (cap %d)", __func__, element, col, col->capacity);
+}
+
+int collection_count(struct collection *col)
+{
+ int i, cnt = 0;
+ for(i=0; i<col->capacity; i++) {
+ if(col->list[i])
+ cnt++;
+ }
+ return cnt;
+}
diff --git a/src/collection.h b/src/collection.h
new file mode 100644
index 0000000..e9b6403
--- /dev/null
+++ b/src/collection.h
@@ -0,0 +1,48 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+
+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
+
+*/
+
+#ifndef __COLLECTION_H__
+#define __COLLECTION_H__
+
+struct collection {
+ void **list;
+ int capacity;
+};
+
+void collection_init(struct collection *col);
+void collection_add(struct collection *col, void *element);
+void collection_remove(struct collection *col, void *element);
+int collection_count(struct collection *col);
+void collection_free(struct collection *col);
+
+#define FOREACH(var, col) \
+ do { \
+ int _iter; \
+ for(_iter=0; _iter<(col)->capacity; _iter++) { \
+ if(!(col)->list[_iter]) continue; \
+ var = (col)->list[_iter];
+
+#define ENDFOREACH \
+ } \
+ } while(0);
+
+#endif
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);
+}
diff --git a/src/sock_stuff.c b/src/sock_stuff.c
new file mode 100644
index 0000000..609c8ad
--- /dev/null
+++ b/src/sock_stuff.c
@@ -0,0 +1,375 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 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 <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+static int wsa_init = 0;
+#else
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#endif
+#include "sock_stuff.h"
+
+#define RECV_TIMEOUT 20000
+
+static int verbose = 0;
+
+void sock_stuff_set_verbose(int level)
+{
+ verbose = level;
+}
+
+#ifndef WIN32
+int create_unix_socket(const char *filename)
+{
+ struct sockaddr_un name;
+ int sock;
+ size_t size;
+
+ // remove if still present
+ unlink(filename);
+
+ /* Create the socket. */
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ /* Bind a name to the socket. */
+ name.sun_family = AF_LOCAL;
+ strncpy(name.sun_path, filename, sizeof(name.sun_path));
+ name.sun_path[sizeof(name.sun_path) - 1] = '\0';
+
+ /* The size of the address is
+ the offset of the start of the filename,
+ plus its length,
+ plus one for the terminating null byte.
+ Alternatively you can just do:
+ size = SUN_LEN (&name);
+ */
+ size = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(name.sun_path) + 1);
+
+ if (bind(sock, (struct sockaddr *) &name, size) < 0) {
+ perror("bind");
+ close_socket(sock);
+ return -1;
+ }
+
+ if (listen(sock, 10) < 0) {
+ perror("listen");
+ close_socket(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+int connect_unix_socket(const char *filename)
+{
+ struct sockaddr_un name;
+ int sfd = -1;
+ size_t size;
+ struct stat fst;
+
+ // check if socket file exists...
+ if (stat(filename, &fst) != 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename,
+ strerror(errno));
+ return -1;
+ }
+ // ... and if it is a unix domain socket
+ if (!S_ISSOCK(fst.st_mode)) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__,
+ filename);
+ return -1;
+ }
+ // make a new socket
+ if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ // and connect to 'filename'
+ name.sun_family = AF_LOCAL;
+ strncpy(name.sun_path, filename, sizeof(name.sun_path));
+ name.sun_path[sizeof(name.sun_path) - 1] = 0;
+
+ size = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(name.sun_path) + 1);
+
+ if (connect(sfd, (struct sockaddr *) &name, size) < 0) {
+ close_socket(sfd);
+ if (verbose >= 2)
+ fprintf(stderr, "%s: connect: %s\n", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return sfd;
+}
+#endif
+
+int create_socket(uint16_t port)
+{
+ int sfd = -1;
+ int yes = 1;
+#ifdef WIN32
+ WSADATA wsa_data;
+ if (!wsa_init) {
+ if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
+ fprintf(stderr, "WSAStartup failed!\n");
+ ExitProcess(-1);
+ }
+ wsa_init = 1;
+ }
+#endif
+ struct sockaddr_in saddr;
+
+ if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
+ perror("socket()");
+ return -1;
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ close_socket(sfd);
+ return -1;
+ }
+
+ memset((void *) &saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons(port);
+
+ if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) {
+ perror("bind()");
+ close_socket(sfd);
+ return -1;
+ }
+
+ if (listen(sfd, 1) == -1) {
+ perror("listen()");
+ close_socket(sfd);
+ return -1;
+ }
+
+ return sfd;
+}
+
+#if defined(WIN32) || defined(__CYGWIN__)
+int connect_socket(const char *addr, uint16_t port)
+{
+ int sfd = -1;
+ int yes = 1;
+ struct hostent *hp;
+ struct sockaddr_in saddr;
+#ifdef WIN32
+ WSADATA wsa_data;
+ if (!wsa_init) {
+ if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
+ fprintf(stderr, "WSAStartup failed!\n");
+ ExitProcess(-1);
+ }
+ wsa_init = 1;
+ }
+#endif
+
+ if (!addr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((hp = gethostbyname(addr)) == NULL) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr);
+ return -1;
+ }
+
+ if (!hp->h_addr) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: gethostbyname returned NULL address!\n",
+ __func__);
+ return -1;
+ }
+
+ if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
+ perror("socket()");
+ return -1;
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ close_socket(sfd);
+ return -1;
+ }
+
+ memset((void *) &saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr;
+ saddr.sin_port = htons(port);
+
+ if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
+ perror("connect");
+ close_socket(sfd);
+ return -2;
+ }
+
+ return sfd;
+}
+#endif /* WIN32 || __CYGWIN__ */
+
+int check_fd(int fd, fd_mode fdm, unsigned int timeout)
+{
+ fd_set fds;
+ int sret;
+ int eagain;
+ struct timeval to;
+ struct timeval *pto;
+
+ if (fd <= 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd);
+ return -1;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if (timeout > 0) {
+ to.tv_sec = (time_t) (timeout / 1000);
+ to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000);
+ pto = &to;
+ } else {
+ pto = NULL;
+ }
+
+ sret = -1;
+
+ do {
+ eagain = 0;
+ switch (fdm) {
+ case FDM_READ:
+ sret = select(fd + 1, &fds, NULL, NULL, pto);
+ break;
+ case FDM_WRITE:
+ sret = select(fd + 1, NULL, &fds, NULL, pto);
+ break;
+ case FDM_EXCEPT:
+ sret = select(fd + 1, NULL, NULL, &fds, pto);
+ break;
+ default:
+ return -1;
+ }
+
+ if (sret < 0) {
+ switch (errno) {
+ case EINTR:
+ // interrupt signal in select
+ if (verbose >= 2)
+ fprintf(stderr, "%s: EINTR\n", __func__);
+ eagain = 1;
+ break;
+ case EAGAIN:
+ if (verbose >= 2)
+ fprintf(stderr, "%s: EAGAIN\n", __func__);
+ break;
+ default:
+ if (verbose >= 2)
+ fprintf(stderr, "%s: select failed: %s\n", __func__,
+ strerror(errno));
+ return -1;
+ }
+ }
+ } while (eagain);
+
+ return sret;
+}
+
+int shutdown_socket(int fd, int how)
+{
+ return shutdown(fd, how);
+}
+
+int close_socket(int fd) {
+#ifdef WIN32
+ return closesocket(fd);
+#else
+ return close(fd);
+#endif
+}
+
+int recv_buf(int fd, void *data, size_t length)
+{
+ return recv_buf_timeout(fd, data, length, 0, RECV_TIMEOUT);
+}
+
+int peek_buf(int fd, void *data, size_t length)
+{
+ return recv_buf_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT);
+}
+
+int recv_buf_timeout(int fd, void *data, size_t length, int flags,
+ unsigned int timeout)
+{
+ int res;
+ int result;
+
+ // check if data is available
+ res = check_fd(fd, FDM_READ, timeout);
+ if (res <= 0) {
+ return res;
+ }
+ // if we get here, there _is_ data available
+ result = recv(fd, data, length, flags);
+ if (res > 0 && result == 0) {
+ // but this is an error condition
+ if (verbose >= 3)
+ fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd);
+ return -EAGAIN;
+ }
+ if (result < 0) {
+ return -errno;
+ }
+ return result;
+}
+
+int send_buf(int fd, void *data, size_t length)
+{
+ return send(fd, data, length, 0);
+}
diff --git a/src/sock_stuff.h b/src/sock_stuff.h
new file mode 100644
index 0000000..5efcd27
--- /dev/null
+++ b/src/sock_stuff.h
@@ -0,0 +1,65 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 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
+
+*/
+
+#ifndef __SOCK_STUFF_H
+#define __SOCK_STUFF_H
+
+#include <stdint.h>
+
+enum fd_mode {
+ FDM_READ,
+ FDM_WRITE,
+ FDM_EXCEPT
+};
+typedef enum fd_mode fd_mode;
+
+#ifdef WIN32
+#include <winsock2.h>
+#define SHUT_RD SD_READ
+#define SHUT_WR SD_WRITE
+#define SHUT_RDWR SD_BOTH
+#endif
+
+#ifndef WIN32
+int create_unix_socket(const char *filename);
+int connect_unix_socket(const char *filename);
+#endif
+int create_socket(uint16_t port);
+#if defined(WIN32) || defined(__CYGWIN__)
+int connect_socket(const char *addr, uint16_t port);
+#endif
+int check_fd(int fd, fd_mode fdm, unsigned int timeout);
+
+int shutdown_socket(int fd, int how);
+int close_socket(int fd);
+
+int recv_buf(int fd, void *data, size_t size);
+int peek_buf(int fd, void *data, size_t size);
+int recv_buf_timeout(int fd, void *data, size_t size, int flags,
+ unsigned int timeout);
+
+int send_buf(int fd, void *data, size_t size);
+
+void sock_stuff_set_verbose(int level);
+
+#endif /* __SOCK_STUFF_H */