From 2f1e0660ef0a3dcfa35c8003b0806bae3acd411d Mon Sep 17 00:00:00 2001 From: Ben Wagner Date: Tue, 11 Feb 2020 14:45:46 -0500 Subject: Add timeout and interrupt handling to debugserver. Fix debugserver_client_handle_response --- include/libimobiledevice/debugserver.h | 17 +++++- src/debugserver.c | 25 ++++++++- src/debugserver.h | 2 + tools/idevicedebug.c | 100 +++++++++++---------------------- 4 files changed, 74 insertions(+), 70 deletions(-) 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 @@ debugserver_error_t debugserver_client_send(debugserver_client_t client, const c * @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); @@ -177,6 +178,20 @@ debugserver_error_t debugserver_client_receive_response(debugserver_client_t cli */ 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. * 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 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_new(idevice_t device 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 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_with_timeout } 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_with_timeout 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 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_ack_mode(debugse 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 @@ static int debugserver_client_receive_internal_check(debugserver_client_t client 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 @@ static void on_signal(int sig) 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 @@ static instproxy_error_t instproxy_client_get_object_by_key_from_info_dictionary 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 @@ static debugserver_error_t debugserver_client_handle_response(debugserver_client 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 @@ static debugserver_error_t debugserver_client_handle_response(debugserver_client 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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); -- cgit v1.1-32-gdbae