diff options
Diffstat (limited to 'libusbmuxd/libusbmuxd.c')
-rw-r--r-- | libusbmuxd/libusbmuxd.c | 333 |
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) { |