summaryrefslogtreecommitdiffstats
path: root/libusbmuxd/libusbmuxd.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2009-08-20 01:19:09 +0200
committerGravatar Hector Martin2009-08-21 03:08:18 +0200
commitc46062aca98f2f077b3bab5c5f72ff2cb57b9dc2 (patch)
tree0934caaa277436a42c515c9ccc86acb004620c7a /libusbmuxd/libusbmuxd.c
parent886d4014509d64023ecf99b57d0fd39818e85bd4 (diff)
downloadusbmuxd-c46062aca98f2f077b3bab5c5f72ff2cb57b9dc2.tar.gz
usbmuxd-c46062aca98f2f077b3bab5c5f72ff2cb57b9dc2.tar.bz2
Updated usbmuxd protocol definition and public header.
[Hector] Merged by putting utils.c into a common dir, avoiding log.c dependency for libusbmuxd, adding CMake magic to tie things up.
Diffstat (limited to 'libusbmuxd/libusbmuxd.c')
-rw-r--r--libusbmuxd/libusbmuxd.c333
1 files changed, 288 insertions, 45 deletions
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) {