Add timeout and interrupt handling to debugserver. Fix debugserver_client_handle_response
diff --git a/include/libimobiledevice/debugserver.h b/include/libimobiledevice/debugserver.h
index ce8176c..03f97a4 100644
--- a/include/libimobiledevice/debugserver.h
+++ b/include/libimobiledevice/debugserver.h
@@ -119,7 +119,8 @@
  * @return DEBUGSERVER_E_SUCCESS on success,
  *      DEBUGSERVER_E_INVALID_ARG when one or more parameters are
  *      invalid, DEBUGSERVER_E_MUX_ERROR when a communication error
- *      occurs, or DEBUGSERVER_E_UNKNOWN_ERROR when an unspecified
+ *      occurs, DEBUGSERVER_E_TIMEOUT when the timeout is reached,
+ *      or DEBUGSERVER_E_UNKNOWN_ERROR when an unspecified
  *      error occurs.
  */
 debugserver_error_t debugserver_client_receive_with_timeout(debugserver_client_t client, char *data, uint32_t size, uint32_t *received, unsigned int timeout);
@@ -178,6 +179,20 @@
 debugserver_error_t debugserver_client_set_ack_mode(debugserver_client_t client, int enabled);
 
 /**
+ * Sets behavior when awaiting a response from the server.
+ *
+ * @see debugserver_client_send_command, debugserver_client_receive_response, debugserver_client_receive
+ *
+ * @param client The debugserver client
+ * @param cancel_receive A function pointer that will be called approximately every receive_loop_timeout milliseconds; the function should return a boolean flag specifying whether to stop waiting for a response. If NULL, behaves as if it always returns true.
+ * @param receive_loop_timeout Time in milliseconds between calls to cancel_receive.
+ *
+ * @return DEBUGSERVER_E_SUCCESS on success, or an DEBUGSERVER_E_* error
+ *     code otherwise.
+ */
+debugserver_error_t debugserver_client_set_receive_params(debugserver_client_t client, int (*cancel_receive)(), int receive_loop_timeout);
+
+/**
  * Sets the argv which launches an app.
  *
  * @param client The debugserver client
diff --git a/src/debugserver.c b/src/debugserver.c
index 46686f6..20ffe01 100644
--- a/src/debugserver.c
+++ b/src/debugserver.c
@@ -89,6 +89,8 @@
 	debugserver_client_t client_loc = (debugserver_client_t) malloc(sizeof(struct debugserver_client_private));
 	client_loc->parent = parent;
 	client_loc->noack_mode = 0;
+        client_loc->cancel_receive = NULL;
+        client_loc->receive_loop_timeout = 1000;
 
 	*client = client_loc;
 
@@ -150,7 +152,7 @@
 	}
 
 	res = debugserver_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout));
-	if (bytes <= 0) {
+	if (bytes <= 0 && res != DEBUGSERVER_E_TIMEOUT) {
 		debug_info("Could not read data, error %d", res);
 	}
 	if (received) {
@@ -162,7 +164,11 @@
 
 LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive(debugserver_client_t client, char* data, uint32_t size, uint32_t *received)
 {
-	return debugserver_client_receive_with_timeout(client, data, size, received, 1000);
+	debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
+  do {
+    res = debugserver_client_receive_with_timeout(client, data, size, received, client->receive_loop_timeout);
+    while (res == DEBUGSERVER_E_TIMEOUT && client->cancel_receive != NULL && !client->cancel_receive());
+    return res;
 }
 
 LIBIMOBILEDEVICE_API debugserver_error_t debugserver_command_new(const char* name, int argc, char* argv[], debugserver_command_t* command)
@@ -355,6 +361,19 @@
 	return DEBUGSERVER_E_SUCCESS;
 }
 
+LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_receive_params(debugserver_client_t client, int (*cancel_receive)(), int receive_loop_timeout)
+{
+	if (!client)
+		return DEBUGSERVER_E_INVALID_ARG;
+
+	client->cancel_receive = cancel_receive;
+	client->receive_loop_timeout = receive_loop_timeout;
+
+	debug_info("receive params: cancel_receive %s, receive_loop_timeout %dms", (client->cancel_receive == NULL ? "unset": "set"), client->receive_loop_timeout);
+
+	return DEBUGSERVER_E_SUCCESS;
+}
+
 static int debugserver_client_receive_internal_check(debugserver_client_t client, char* received_char)
 {
 	debugserver_error_t res = DEBUGSERVER_E_SUCCESS;
@@ -363,7 +382,7 @@
 	uint32_t bytes = 0;
 
 	/* we loop here as we expect an answer */
-	res = debugserver_client_receive_with_timeout(client, &buffer, sizeof(char), &bytes, 1000);
+	res = debugserver_client_receive(client, &buffer, sizeof(char), &bytes);
 	if (res == DEBUGSERVER_E_SUCCESS && received_char[0] != 0) {
 		if (memcmp(&buffer, received_char, sizeof(char)) == 0) {
 			did_receive_char = 1;
diff --git a/src/debugserver.h b/src/debugserver.h
index 05cd97b..ee3ba62 100644
--- a/src/debugserver.h
+++ b/src/debugserver.h
@@ -30,6 +30,8 @@
 struct debugserver_client_private {
 	service_client_t parent;
 	int noack_mode;
+	int (*cancel_receive)();
+	int receive_loop_timeout;
 };
 
 struct debugserver_command_private {
diff --git a/tools/idevicedebug.c b/tools/idevicedebug.c
index ca4cb18..4c1ca89 100644
--- a/tools/idevicedebug.c
+++ b/tools/idevicedebug.c
@@ -61,6 +61,11 @@
 	quit_flag++;
 }
 
+static void cancel_receive()
+{
+	return quit_flag;
+}
+
 static instproxy_error_t instproxy_client_get_object_by_key_from_info_dictionary_for_bundle_identifier(instproxy_client_t client, const char* appid, const char* key, plist_t* node)
 {
 	if (!client || !appid || !key)
@@ -108,10 +113,9 @@
 	return INSTPROXY_E_SUCCESS;
 }
 
-static debugserver_error_t debugserver_client_handle_response(debugserver_client_t client, char** response, int send_reply, int* exit_status)
+static debugserver_error_t debugserver_client_handle_response(debugserver_client_t client, char** response, int* exit_status)
 {
 	debugserver_error_t dres = DEBUGSERVER_E_SUCCESS;
-	debugserver_command_t command = NULL;
 	char* o = NULL;
 	char* r = *response;
 
@@ -123,49 +127,13 @@
 		debugserver_decode_string(r + 1, strlen(r) - 1, &o);
 		printf("%s", o);
 		fflush(stdout);
-		if (o != NULL) {
-			free(o);
-			o = NULL;
-		}
-
-		free(*response);
-		*response = NULL;
-
-		if (!send_reply)
-			return dres;
-
-		/* send reply */
-		debugserver_command_new("OK", 0, NULL, &command);
-		dres = debugserver_client_send_command(client, command, response, NULL);
-		log_debug("result: %d", dres);
-		debugserver_command_free(command);
-		command = NULL;
 	} else if (r[0] == 'T') {
 		/* thread stopped information */
 		log_debug("Thread stopped. Details:\n%s", r + 1);
-
-		free(*response);
-		*response = NULL;
-
-		if (!send_reply)
-			return dres;
-
+                /* Break out of the loop. */
 		dres = DEBUGSERVER_E_UNKNOWN_ERROR;
 	} else if (r[0] == 'E') {
 		printf("ERROR: %s\n", r + 1);
-
-		free(*response);
-		*response = NULL;
-
-		if (!send_reply)
-			return dres;
-
-		/* send reply */
-		debugserver_command_new("OK", 0, NULL, &command);
-		dres = debugserver_client_send_command(client, command, response, NULL);
-		log_debug("result: %d", dres);
-		debugserver_command_free(command);
-		command = NULL;
 	} else if (r[0] == 'W' || r[0] == 'X') {
 		/* process exited */
 		debugserver_decode_string(r + 1, strlen(r) - 1, &o);
@@ -173,29 +141,23 @@
 			printf("Exit %s: %u\n", (r[0] == 'W' ? "status" : "due to signal"), o[0]);
 			/* Use bash convention where signals cause an exit status of 128 + signal */
 			*exit_status = o[0] + (r[0] == 'W' ? 0 : 128);
-			free(o);
-			o = NULL;
-		}
-		free(*response);
-		*response = NULL;
-		return dres;
+		} else {
+                  debug_info("Unable to decode exit status from %s", r);
+                  dres = DEBUGSERVER_E_UNKNOWN_ERROR;
+                }
 	} else if (r && strlen(r) == 0) {
-		if (!send_reply)
-			return dres;
-
-		free(*response);
-		*response = NULL;
-
-		/* no command */
-		debugserver_command_new("OK", 0, NULL, &command);
-		dres = debugserver_client_send_command(client, command, response, NULL);
-		log_debug("result: %d", dres);
-		debugserver_command_free(command);
-		command = NULL;
+		log_debug("empty response");
 	} else {
 		log_debug("ERROR: unhandled response '%s'", r);
 	}
 
+        if (o != NULL) {
+          free(o);
+          o = NULL;
+        }
+
+        free(*response);
+        *response = NULL;
 	return dres;
 }
 
@@ -383,6 +345,12 @@
 				goto cleanup;
 			}
 
+                        /* set receive params */
+                        if (debugserver_client_set_receive_params(debugserver_client, cancel_receive, 250) != DEBUGSERVER_E_SUCCESS) {
+				fprintf(stderr, "Error in debugserver_client_set_receive_params\n");
+				goto cleanup;
+			}
+
 			/* enable logging for the session in debug mode */
 			if (debug_level) {
 				log_debug("Setting logging bitmask...");
@@ -392,7 +360,7 @@
 				command = NULL;
 				if (response) {
 					if (strncmp(response, "OK", 2)) {
-						debugserver_client_handle_response(debugserver_client, &response, 0, NULL);
+						debugserver_client_handle_response(debugserver_client, &response, NULL);
 						goto cleanup;
 					}
 					free(response);
@@ -410,7 +378,7 @@
 			command = NULL;
 			if (response) {
 				if (strncmp(response, "OK", 2)) {
-					debugserver_client_handle_response(debugserver_client, &response, 0, NULL);
+					debugserver_client_handle_response(debugserver_client, &response, NULL);
 					goto cleanup;
 				}
 				free(response);
@@ -426,7 +394,7 @@
 			command = NULL;
 			if (response) {
 				if (strncmp(response, "OK", 2)) {
-					debugserver_client_handle_response(debugserver_client, &response, 0, NULL);
+					debugserver_client_handle_response(debugserver_client, &response, NULL);
 					goto cleanup;
 				}
 				free(response);
@@ -467,7 +435,7 @@
 			command = NULL;
 			if (response) {
 				if (strncmp(response, "OK", 2)) {
-					debugserver_client_handle_response(debugserver_client, &response, 0, NULL);
+					debugserver_client_handle_response(debugserver_client, &response, NULL);
 					goto cleanup;
 				}
 				free(response);
@@ -493,7 +461,7 @@
 			command = NULL;
 			if (response) {
 				if (strncmp(response, "OK", 2)) {
-					debugserver_client_handle_response(debugserver_client, &response, 0, NULL);
+					debugserver_client_handle_response(debugserver_client, &response, NULL);
 					goto cleanup;
 				}
 				free(response);
@@ -511,21 +479,21 @@
 			log_debug("Entering run loop...");
 			while (!quit_flag) {
 				if (dres != DEBUGSERVER_E_SUCCESS) {
-					log_debug("failed to receive response");
+					log_debug("failed to receive response; error %d", dres);
 					break;
 				}
 
 				if (response) {
 					log_debug("response: %s", response);
 					if (strncmp(response, "OK", 2)) {
-						dres = debugserver_client_handle_response(debugserver_client, &response, 1, &res);
+						dres = debugserver_client_handle_response(debugserver_client, &response, &res);
 					}
 				}
 				if (res >= 0) {
 					goto cleanup;
 				}
 
-				sleep(1);
+				dres = debugserver_client_receive(debugserver_client, &response, NULL);
 			}
 
 			/* kill process after we finished */
@@ -536,7 +504,7 @@
 			command = NULL;
 			if (response) {
 				if (strncmp(response, "OK", 2)) {
-					debugserver_client_handle_response(debugserver_client, &response, 0, NULL);
+					debugserver_client_handle_response(debugserver_client, &response, NULL);
 					goto cleanup;
 				}
 				free(response);