summaryrefslogtreecommitdiffstats
path: root/libusbmuxd
diff options
context:
space:
mode:
Diffstat (limited to 'libusbmuxd')
-rw-r--r--libusbmuxd/CMakeLists.txt4
-rw-r--r--libusbmuxd/libusbmuxd.c333
-rw-r--r--libusbmuxd/usbmuxd-proto.h58
-rw-r--r--libusbmuxd/usbmuxd.h61
4 files changed, 372 insertions, 84 deletions
diff --git a/libusbmuxd/CMakeLists.txt b/libusbmuxd/CMakeLists.txt
index 61de1a8..7f7b35f 100644
--- a/libusbmuxd/CMakeLists.txt
+++ b/libusbmuxd/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_library (libusbmuxd libusbmuxd.c sock_stuff.c)
+include_directories (${CMAKE_SOURCE_DIR}/common)
+
+add_library (libusbmuxd libusbmuxd.c sock_stuff.c ../common/utils.c)
# 'lib' is a UNIXism, the proper CMake target is usbmuxd
# But we can't use that due to the conflict with the usbmuxd daemon,
diff --git a/libusbmuxd/libusbmuxd.c b/libusbmuxd/libusbmuxd.c
index 090695f..6a54765 100644
--- a/libusbmuxd/libusbmuxd.c
+++ b/libusbmuxd/libusbmuxd.c
@@ -6,6 +6,7 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
+#include <signal.h>
// usbmuxd public interface
#include "usbmuxd.h"
@@ -13,10 +14,48 @@
#include "usbmuxd-proto.h"
// socket utility functions
#include "sock_stuff.h"
+// misc utility functions
+#include "utils.h"
+static struct collection devices;
+static usbmuxd_event_cb_t event_cb = NULL;
+pthread_t devmon;
+static int listenfd = -1;
+
+/**
+ * Finds a device info record by its handle.
+ * if the record is not found, NULL is returned.
+ */
+static usbmuxd_device_info_t *devices_find(int 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()
+{
+#ifdef WINDOWS
+ return connect_socket("127.0.0.1", 27015);
+#else
+ return connect_unix_socket(USBMUXD_SOCKET_FILE);
+#endif
+}
+
+/**
+ * Retrieves the result code to a previously sent request.
+ */
static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result)
{
- struct usbmuxd_result res;
+ struct usbmuxd_result_msg res;
int recv_len;
if (!result) {
@@ -29,8 +68,8 @@ static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result)
} else {
if ((recv_len == sizeof(res))
&& (res.header.length == (uint32_t) recv_len)
- && (res.header.reserved == 0)
- && (res.header.type == USBMUXD_RESULT)
+ && (res.header.version == USBMUXD_PROTOCOL_VERSION)
+ && (res.header.message == MESSAGE_RESULT)
) {
*result = res.result;
if (res.header.tag == tag) {
@@ -44,16 +83,229 @@ static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result)
return -1;
}
-int usbmuxd_scan(usbmuxd_scan_result ** available_devices)
+/**
+ * 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_device_event event)
+{
+ usbmuxd_event_t ev;
+
+ if (!callback || !dev) {
+ return;
+ }
+
+ ev.event = event;
+ memcpy(&ev.device, dev, sizeof(usbmuxd_device_info_t));
+
+ printf("%s: event=%d, handle=%d\n", __func__, ev.event, ev.device.handle);
+
+ callback(&ev);
+}
+
+/**
+ * Tries to connect to usbmuxd and wait if it is not running.
+ *
+ * TODO inotify support should come here
+ */
+static int usbmuxd_listen()
{
- struct usbmuxd_scan_request s_req;
int sfd;
- int scan_success = 0;
+ uint32_t res = -1;
+ struct usbmuxd_listen_request req;
+ struct usbmuxd_header hdr;
+
+ req.header.length = sizeof(struct usbmuxd_listen_request);
+ req.header.version = USBMUXD_PROTOCOL_VERSION;
+ req.header.message = MESSAGE_LISTEN;
+ req.header.tag = 2;
+
+ sfd = connect_usbmuxd_socket();
+ if (sfd < 0) {
+ fprintf(stderr, "DEBUG: waiting for usbmuxd to come up.\n");
+
+ while (event_cb) {
+ if ((sfd = connect_usbmuxd_socket()) > 0) {
+ fprintf(stderr, "DEBUG: usbmuxd started\n");
+ break;
+ }
+ sleep(1);
+ }
+ }
+
+ if (sfd < 0) {
+ fprintf(stderr, "ERROR: usbmuxd was supposed to be running here...\n");
+ return sfd;
+ }
+
+ if (send_buf(sfd, &req, req.header.length) != (int)req.header.length) {
+ fprintf(stderr, "ERROR: could not send listen packet\n");
+ close(sfd);
+ return -1;
+ }
+ if (usbmuxd_get_result(sfd, req.header.tag, &res) && (res != 0)) {
+ fprintf(stderr, "ERROR: did not get OK\n");
+ close(sfd);
+ return -1;
+ }
+
+ 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.
+ */
+int get_next_event(int sfd, usbmuxd_event_cb_t callback)
+{
+ int recv_len;
+ struct usbmuxd_listen_request req;
+ struct usbmuxd_header hdr;
+
+ /* block until we receive something */
+ recv_len = recv_buf_timeout(sfd, &hdr, sizeof(hdr), 0, 0);
+ if (recv_len < 0) {
+ int i;
+ fprintf(stderr, "DEBUG: connection closed.\n");
+ // when then usbmuxd connection fails,
+ // generate remove events for every device that
+ // is still present so applications know about it
+ // TODO: is this behaviour correct?
+ FOREACH(usbmuxd_device_info_t *dev, &devices) {
+ generate_event(callback, dev, UE_DEVICE_REMOVE);
+ } ENDFOREACH
+ collection_free(&devices);
+ return recv_len;
+ } else if (recv_len == sizeof(hdr)) {
+ if (hdr.message == MESSAGE_DEVICE_ADD) {
+ struct usbmuxd_device_record dev;
+ usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t));
+ if (!devinfo) {
+ fprintf(stderr, "Out of memory!\n");
+ return -1;
+ }
+
+ if (hdr.length != sizeof(struct usbmuxd_header)+sizeof(struct usbmuxd_device_record)) {
+ fprintf(stderr, "WARNING: unexpected packet size%d for MESSAGE_DEVICE_ADD (expected %d)!\n", hdr.length, sizeof(struct usbmuxd_header)+sizeof(struct usbmuxd_device_record));
+ }
+ recv_len = recv_buf_timeout(sfd, &dev, hdr.length - sizeof(struct usbmuxd_header), 0, 5000);
+ if (recv_len != (hdr.length - sizeof(struct usbmuxd_header))) {
+ fprintf(stderr, "Could not receive packet\n");
+ return recv_len;
+ }
+
+ devinfo->handle = dev.device_id;
+ devinfo->product_id = dev.product_id;
+ memset(devinfo->uuid, '\0', sizeof(devinfo->uuid));
+ memcpy(devinfo->uuid, dev.serial_number, sizeof(devinfo->uuid));
+
+ collection_add(&devices, devinfo);
+ generate_event(callback, devinfo, UE_DEVICE_ADD);
+ } else if (hdr.message == MESSAGE_DEVICE_REMOVE) {
+ uint32_t handle;
+ usbmuxd_device_info_t *dev;
+
+ if (hdr.length != sizeof(struct usbmuxd_header)+sizeof(uint32_t)) {
+ fprintf(stderr, "WARNING: unexpected packet size%d for MESSAGE_DEVICE_REMOVE (expected %d)!\n", hdr.length, sizeof(struct usbmuxd_header)+sizeof(uint32_t));
+ }
+ recv_len = recv_buf_timeout(sfd, &handle, sizeof(uint32_t), 0, 5000);
+ if (recv_len != sizeof(uint32_t)) {
+ fprintf(stderr, "Could not receive packet\n");
+ return recv_len;
+ }
+
+ dev = devices_find(handle);
+ if (!dev) {
+ fprintf(stderr, "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", handle);
+ } else {
+ generate_event(callback, dev, UE_DEVICE_REMOVE);
+ collection_remove(&devices, dev);
+ }
+ } else {
+ fprintf(stderr, "%s: Unknown message type %d length %d\n", __func__, hdr.message, hdr.length);
+ }
+ } else {
+ fprintf(stderr, "%s: ERROR: incomplete packet received!\n", __func__);
+ }
+ return 0;
+}
+
+/**
+ * Device Monitor thread function.
+ *
+ * This function sets up a connection to usbmuxd
+ */
+static void *device_monitor(void *data)
+{
+ collection_init(&devices);
+
+ while (event_cb) {
+
+ listenfd = usbmuxd_listen();
+ if (listenfd < 0) {
+ fprintf(stderr, "DEBUG: listenfd=%d\n", listenfd);
+ continue;
+ }
+
+ while (event_cb) {
+ printf("waiting for events\n");
+ int res = get_next_event(listenfd, event_cb);
+ if (res < 0) {
+ fprintf(stderr, "%s: closing connection (code %d)\n", __func__, res);
+ break;
+ }
+ }
+ }
+
+ collection_free(&devices);
+ printf("%s: terminated\n", __func__);
+
+ return NULL;
+}
+
+int usbmuxd_subscribe(usbmuxd_event_cb_t callback)
+{
+ int res;
+
+ if (!callback) {
+ return -EINVAL;
+ }
+ event_cb = callback;
+
+ res = pthread_create(&devmon, NULL, device_monitor, NULL);
+ if (res != 0) {
+ fprintf(stderr, "ERROR: Could not start device watcher thread!\n");
+ return res;
+ }
+ return 0;
+}
+
+int usbmuxd_unsubscribe()
+{
+ event_cb = NULL;
+
+ if (pthread_kill(devmon, 0) == 0) {
+ printf("%s: unsubscribing callback\n", __func__);
+ close(listenfd);
+ listenfd = -1;
+ pthread_kill(devmon, SIGINT);
+ pthread_join(devmon, NULL);
+ }
+
+ return 0;
+}
+
+int usbmuxd_scan(usbmuxd_device_info_t ** available_devices)
+{
+ struct usbmuxd_listen_request s_req;
+ int sfd;
+ int listen_success = 0;
uint32_t res;
- uint32_t pktlen;
int recv_len;
- usbmuxd_scan_result *newlist = NULL;
- struct usbmuxd_device_info_record dev_info_pkt;
+ usbmuxd_device_info_t *newlist = NULL;
+ struct usbmuxd_header hdr;
+ struct usbmuxd_device_record dev_info;
int dev_cnt = 0;
sfd = connect_unix_socket(USBMUXD_SOCKET_FILE);
@@ -62,9 +314,9 @@ int usbmuxd_scan(usbmuxd_scan_result ** available_devices)
return sfd;
}
- s_req.header.length = sizeof(struct usbmuxd_scan_request);
- s_req.header.reserved = 0;
- s_req.header.type = USBMUXD_SCAN;
+ s_req.header.length = sizeof(struct usbmuxd_listen_request);
+ s_req.header.version = USBMUXD_PROTOCOL_VERSION;
+ s_req.header.message = MESSAGE_LISTEN;
s_req.header.tag = 2;
// send scan request packet
@@ -73,7 +325,7 @@ int usbmuxd_scan(usbmuxd_scan_result ** available_devices)
res = -1;
// get response
if (usbmuxd_get_result(sfd, s_req.header.tag, &res) && (res == 0)) {
- scan_success = 1;
+ listen_success = 1;
} else {
fprintf(stderr,
"%s: Did not get response to scan request (with result=0)...\n",
@@ -83,50 +335,44 @@ int usbmuxd_scan(usbmuxd_scan_result ** available_devices)
}
}
- if (!scan_success) {
- fprintf(stderr, "%s: Could not send scan request!\n", __func__);
+ if (!listen_success) {
+ fprintf(stderr, "%s: Could not send listen request!\n", __func__);
return -1;
}
*available_devices = NULL;
// receive device list
while (1) {
- if (recv_buf_timeout(sfd, &pktlen, 4, MSG_PEEK, 1000) == 4) {
- if (pktlen != sizeof(dev_info_pkt)) {
+ if (recv_buf_timeout(sfd, &hdr, sizeof(hdr), 0, 1000) == sizeof(hdr)) {
+ if (hdr.length != sizeof(hdr)+sizeof(dev_info)) {
// invalid packet size received!
fprintf(stderr,
"%s: Invalid packet size (%d) received when expecting a device info record.\n",
- __func__, pktlen);
+ __func__, hdr.length);
break;
}
- recv_len = recv_buf(sfd, &dev_info_pkt, pktlen);
+ recv_len = recv_buf(sfd, &dev_info, hdr.length - sizeof(hdr));
if (recv_len <= 0) {
fprintf(stderr,
"%s: Error when receiving device info record\n",
__func__);
break;
- } else if ((uint32_t) recv_len < pktlen) {
+ } else if ((uint32_t) recv_len < hdr.length - sizeof(hdr)) {
fprintf(stderr,
- "%s: received less data than specified in header!\n",
- __func__);
+ "%s: received less data than specified in header!\n", __func__);
} else {
- //fprintf(stderr, "%s: got device record with id %d, UUID=%s\n", __func__, dev_info_pkt.device_info.device_id, dev_info_pkt.device_info.serial_number);
- newlist =
- (usbmuxd_scan_result *) realloc(*available_devices,
- sizeof
- (usbmuxd_scan_result) *
- (dev_cnt + 1));
+ newlist = (usbmuxd_device_info_t *) realloc(*available_devices, sizeof(usbmuxd_device_info_t) * (dev_cnt + 1));
if (newlist) {
newlist[dev_cnt].handle =
- (int) dev_info_pkt.device.device_id;
+ (int) dev_info.device_id;
newlist[dev_cnt].product_id =
- dev_info_pkt.device.product_id;
- memset(newlist[dev_cnt].serial_number, '\0',
- sizeof(newlist[dev_cnt].serial_number));
- memcpy(newlist[dev_cnt].serial_number,
- dev_info_pkt.device.serial_number,
- sizeof(dev_info_pkt.device.serial_number));
+ dev_info.product_id;
+ memset(newlist[dev_cnt].uuid, '\0',
+ sizeof(newlist[dev_cnt].uuid));
+ memcpy(newlist[dev_cnt].uuid,
+ dev_info.serial_number,
+ sizeof(newlist[dev_cnt].uuid));
*available_devices = newlist;
dev_cnt++;
} else {
@@ -144,24 +390,21 @@ int usbmuxd_scan(usbmuxd_scan_result ** available_devices)
}
// terminating zero record
- newlist =
- (usbmuxd_scan_result *) realloc(*available_devices,
- sizeof(usbmuxd_scan_result) *
- (dev_cnt + 1));
- memset(newlist + dev_cnt, 0, sizeof(usbmuxd_scan_result));
+ newlist = (usbmuxd_device_info_t*) realloc(*available_devices, sizeof(usbmuxd_device_info_t) * (dev_cnt + 1));
+ memset(newlist + dev_cnt, 0, sizeof(usbmuxd_device_info_t));
*available_devices = newlist;
return dev_cnt;
}
-int usbmuxd_connect(const int handle, const unsigned short tcp_port)
+int usbmuxd_connect(const int handle, const unsigned short port)
{
int sfd;
struct usbmuxd_connect_request c_req;
int connected = 0;
uint32_t res = -1;
- sfd = connect_unix_socket(USBMUXD_SOCKET_FILE);
+ sfd = connect_usbmuxd_socket();
if (sfd < 0) {
fprintf(stderr, "%s: Error: Connection to usbmuxd failed: %s\n",
__func__, strerror(errno));
@@ -169,11 +412,11 @@ int usbmuxd_connect(const int handle, const unsigned short tcp_port)
}
c_req.header.length = sizeof(c_req);
- c_req.header.reserved = 0;
- c_req.header.type = USBMUXD_CONNECT;
+ c_req.header.version = USBMUXD_PROTOCOL_VERSION;
+ c_req.header.message = MESSAGE_CONNECT;
c_req.header.tag = 3;
c_req.device_id = (uint32_t) handle;
- c_req.tcp_dport = htons(tcp_port);
+ c_req.port = htons(port);
c_req.reserved = 0;
if (send_buf(sfd, &c_req, sizeof(c_req)) < 0) {
diff --git a/libusbmuxd/usbmuxd-proto.h b/libusbmuxd/usbmuxd-proto.h
index 7f8c2d6..1ecb7bc 100644
--- a/libusbmuxd/usbmuxd-proto.h
+++ b/libusbmuxd/usbmuxd-proto.h
@@ -1,52 +1,62 @@
/* Protocol defintion for usbmuxd proxy protocol */
-
#ifndef __USBMUXD_PROTO_H
#define __USBMUXD_PROTO_H
#include <stdint.h>
+#define USBMUXD_PROTOCOL_VERSION 0
#define USBMUXD_SOCKET_FILE "/var/run/usbmuxd"
+enum usbmuxd_result {
+ RESULT_OK = 0,
+ RESULT_BADCOMMAND = 1,
+ RESULT_BADDEV = 2,
+ RESULT_CONNREFUSED = 3,
+ // ???
+ // ???
+ RESULT_BADVERSION = 6,
+};
+
+enum usbmuxd_msgtype {
+ MESSAGE_RESULT = 1,
+ MESSAGE_CONNECT = 2,
+ MESSAGE_LISTEN = 3,
+ MESSAGE_DEVICE_ADD = 4,
+ MESSAGE_DEVICE_REMOVE = 5,
+ //???
+ //???
+ //MESSAGE_PLIST = 8,
+};
+
struct usbmuxd_header {
uint32_t length; // length of message, including header
- uint32_t reserved; // always zero
- uint32_t type; // message type
+ uint32_t version; // protocol version
+ uint32_t message; // message type
uint32_t tag; // responses to this query will echo back this tag
} __attribute__((__packed__));
-struct usbmuxd_result {
+struct usbmuxd_result_msg {
struct usbmuxd_header header;
uint32_t result;
} __attribute__((__packed__));
-struct usbmuxd_connect_request {
+struct usbmuxd_connect_request {
struct usbmuxd_header header;
uint32_t device_id;
- uint16_t tcp_dport; // TCP port number
+ uint16_t port; // TCP port number
uint16_t reserved; // set to zero
} __attribute__((__packed__));
-struct usbmuxd_device {
- uint32_t device_id;
- uint16_t product_id;
- char serial_number[40];
-} __attribute__((__packed__));
-
-struct usbmuxd_device_info_record {
+struct usbmuxd_listen_request {
struct usbmuxd_header header;
- struct usbmuxd_device device;
- char padding[222];
} __attribute__((__packed__));
-struct usbmuxd_scan_request {
- struct usbmuxd_header header;
+struct usbmuxd_device_record {
+ uint32_t device_id;
+ uint16_t product_id;
+ char serial_number[256];
+ uint16_t padding;
+ uint32_t location;
} __attribute__((__packed__));
-enum {
- USBMUXD_RESULT = 1,
- USBMUXD_CONNECT = 2,
- USBMUXD_SCAN = 3,
- USBMUXD_DEVICE_INFO = 4,
-};
-
#endif /* __USBMUXD_PROTO_H */
diff --git a/libusbmuxd/usbmuxd.h b/libusbmuxd/usbmuxd.h
index ba45ec3..f12ae39 100644
--- a/libusbmuxd/usbmuxd.h
+++ b/libusbmuxd/usbmuxd.h
@@ -2,10 +2,7 @@
#define __USBMUXD_H
/**
- * Array entry returned by 'usbmuxd_scan()' scanning.
- *
- * If more than one device is available, 'product_id' and
- * 'serial_number' and be analysed to help make a selection.
+ * Device information structure holding data to identify the device.
* The relevant 'handle' should be passed to 'usbmuxd_connect()', to
* start a proxy connection. The value 'handle' should be considered
* opaque and no presumption made about the meaning of its value.
@@ -13,22 +10,58 @@
typedef struct {
int handle;
int product_id;
- char serial_number[41];
-} usbmuxd_scan_result;
+ char uuid[41];
+} usbmuxd_device_info_t;
+
+/**
+ * event types for event callback function
+ */
+enum usbmuxd_device_event {
+ UE_DEVICE_ADD = 1,
+ UE_DEVICE_REMOVE
+};
+
+/**
+ * Event structure that will be passed to the callback function.
+ * 'event' will contains the type of the event, and 'device' will contains
+ * information about the device.
+ */
+typedef struct {
+ int event;
+ usbmuxd_device_info_t device;
+} usbmuxd_event_t;
+
+/**
+ * Callback function prototype.
+ */
+typedef void (*usbmuxd_event_cb_t) (const usbmuxd_event_t *event);
+
+/**
+ * Subscribe a callback function so that applications get to know about
+ * device add/remove events.
+ *
+ * @param callback A callback function that is executed when an event occurs.
+ *
+ * @return 0 on success or negative on error.
+ */
+int usbmuxd_subscribe(usbmuxd_event_cb_t callback);
+
+/**
+ * Unsubscribe callback.
+ *
+ * @return only 0 for now.
+ */
+int usbmuxd_unsubscribe();
/**
- * Contacts usbmuxd and performs a scan for connected devices.
+ * Contacts usbmuxd and retrieves a list of connected devices.
*
- * @param available_devices pointer to array of usbmuxd_scan_result.
- * Array of available devices. The required 'handle'
- * should be passed to 'usbmuxd_connect()'. The returned array
- * is zero-terminated for convenience; the final (unused)
- * entry containing handle == 0. The returned array pointer
- * should be freed by passing to 'free()' after use.
+ * @param available_devices pointer to an array of usbmuxd_device_info_t
+ * that will hold records of the connected devices.
*
* @return number of available devices, zero on no devices, or negative on error
*/
-int usbmuxd_scan(usbmuxd_scan_result **available_devices);
+int usbmuxd_scan(usbmuxd_device_info_t **available_devices);
/**
* Request proxy connect to