diff options
Diffstat (limited to 'tools/idevicedebug.c')
-rw-r--r-- | tools/idevicedebug.c | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/tools/idevicedebug.c b/tools/idevicedebug.c new file mode 100644 index 0000000..36c594e --- /dev/null +++ b/tools/idevicedebug.c | |||
@@ -0,0 +1,625 @@ | |||
1 | /* | ||
2 | * idevicedebug.c | ||
3 | * Interact with the debugserver service of a device. | ||
4 | * | ||
5 | * Copyright (c) 2014-2015 Martin Szulecki All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | |||
26 | #define TOOL_NAME "idevicedebug" | ||
27 | |||
28 | #include <signal.h> | ||
29 | #include <stdio.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #include <time.h> | ||
33 | #include <unistd.h> | ||
34 | #include <libgen.h> | ||
35 | #include <getopt.h> | ||
36 | |||
37 | #ifdef WIN32 | ||
38 | #include <windows.h> | ||
39 | #define sleep(x) Sleep(x*1000) | ||
40 | #endif | ||
41 | |||
42 | #include <libimobiledevice/installation_proxy.h> | ||
43 | #include <libimobiledevice/libimobiledevice.h> | ||
44 | #include <libimobiledevice/debugserver.h> | ||
45 | #include <plist/plist.h> | ||
46 | #include "common/debug.h" | ||
47 | |||
48 | static int debug_level = 0; | ||
49 | |||
50 | #define log_debug(...) if (debug_level > 0) { printf(__VA_ARGS__); fputc('\n', stdout); } | ||
51 | |||
52 | enum cmd_mode { | ||
53 | CMD_NONE = 0, | ||
54 | CMD_RUN, | ||
55 | CMD_KILL | ||
56 | }; | ||
57 | |||
58 | static int quit_flag = 0; | ||
59 | |||
60 | static void on_signal(int sig) | ||
61 | { | ||
62 | fprintf(stderr, "Exiting...\n"); | ||
63 | quit_flag++; | ||
64 | } | ||
65 | |||
66 | static int cancel_receive() | ||
67 | { | ||
68 | return quit_flag; | ||
69 | } | ||
70 | |||
71 | 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) | ||
72 | { | ||
73 | if (!client || !appid || !key) | ||
74 | return INSTPROXY_E_INVALID_ARG; | ||
75 | |||
76 | plist_t apps = NULL; | ||
77 | |||
78 | // create client options for any application types | ||
79 | plist_t client_opts = instproxy_client_options_new(); | ||
80 | instproxy_client_options_add(client_opts, "ApplicationType", "Any", NULL); | ||
81 | |||
82 | // only return attributes we need | ||
83 | instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "CFBundleExecutable", key, NULL); | ||
84 | |||
85 | // only query for specific appid | ||
86 | const char* appids[] = {appid, NULL}; | ||
87 | |||
88 | // query device for list of apps | ||
89 | instproxy_error_t ierr = instproxy_lookup(client, appids, client_opts, &apps); | ||
90 | |||
91 | instproxy_client_options_free(client_opts); | ||
92 | |||
93 | if (ierr != INSTPROXY_E_SUCCESS) { | ||
94 | return ierr; | ||
95 | } | ||
96 | |||
97 | plist_t app_found = plist_access_path(apps, 1, appid); | ||
98 | if (!app_found) { | ||
99 | if (apps) | ||
100 | plist_free(apps); | ||
101 | *node = NULL; | ||
102 | return INSTPROXY_E_OP_FAILED; | ||
103 | } | ||
104 | |||
105 | plist_t object = plist_dict_get_item(app_found, key); | ||
106 | if (object) { | ||
107 | *node = plist_copy(object); | ||
108 | } else { | ||
109 | log_debug("key %s not found", key); | ||
110 | return INSTPROXY_E_OP_FAILED; | ||
111 | } | ||
112 | |||
113 | plist_free(apps); | ||
114 | |||
115 | return INSTPROXY_E_SUCCESS; | ||
116 | } | ||
117 | |||
118 | static debugserver_error_t debugserver_client_handle_response(debugserver_client_t client, char** response, int* exit_status) | ||
119 | { | ||
120 | debugserver_error_t dres = DEBUGSERVER_E_SUCCESS; | ||
121 | char* o = NULL; | ||
122 | char* r = *response; | ||
123 | |||
124 | /* Documentation of response codes can be found here: | ||
125 | https://github.com/llvm/llvm-project/blob/4fe839ef3a51e0ea2e72ea2f8e209790489407a2/lldb/docs/lldb-gdb-remote.txt#L1269 | ||
126 | */ | ||
127 | |||
128 | if (r[0] == 'O') { | ||
129 | /* stdout/stderr */ | ||
130 | debugserver_decode_string(r + 1, strlen(r) - 1, &o); | ||
131 | printf("%s", o); | ||
132 | fflush(stdout); | ||
133 | } else if (r[0] == 'T') { | ||
134 | /* thread stopped information */ | ||
135 | log_debug("Thread stopped. Details:\n%s", r + 1); | ||
136 | if (exit_status != NULL) { | ||
137 | /* "Thread stopped" seems to happen when assert() fails. | ||
138 | Use bash convention where signals cause an exit | ||
139 | status of 128 + signal | ||
140 | */ | ||
141 | *exit_status = 128 + SIGABRT; | ||
142 | } | ||
143 | /* Break out of the loop. */ | ||
144 | dres = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
145 | } else if (r[0] == 'E') { | ||
146 | printf("ERROR: %s\n", r + 1); | ||
147 | } else if (r[0] == 'W' || r[0] == 'X') { | ||
148 | /* process exited */ | ||
149 | debugserver_decode_string(r + 1, strlen(r) - 1, &o); | ||
150 | if (o != NULL) { | ||
151 | printf("Exit %s: %u\n", (r[0] == 'W' ? "status" : "due to signal"), o[0]); | ||
152 | if (exit_status != NULL) { | ||
153 | /* Use bash convention where signals cause an | ||
154 | exit status of 128 + signal | ||
155 | */ | ||
156 | *exit_status = o[0] + (r[0] == 'W' ? 0 : 128); | ||
157 | } | ||
158 | } else { | ||
159 | log_debug("Unable to decode exit status from %s", r); | ||
160 | dres = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
161 | } | ||
162 | } else if (r && strlen(r) == 0) { | ||
163 | log_debug("empty response"); | ||
164 | } else { | ||
165 | log_debug("ERROR: unhandled response '%s'", r); | ||
166 | } | ||
167 | |||
168 | if (o != NULL) { | ||
169 | free(o); | ||
170 | o = NULL; | ||
171 | } | ||
172 | |||
173 | free(*response); | ||
174 | *response = NULL; | ||
175 | return dres; | ||
176 | } | ||
177 | |||
178 | static void print_usage(int argc, char **argv, int is_error) | ||
179 | { | ||
180 | char *name = strrchr(argv[0], '/'); | ||
181 | fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0])); | ||
182 | fprintf(is_error ? stderr : stdout, | ||
183 | "\n" | ||
184 | "Interact with the debugserver service of a device.\n" | ||
185 | "\n" | ||
186 | "Where COMMAND is one of:\n" | ||
187 | " run BUNDLEID [ARGS...] run app with BUNDLEID and optional ARGS on device.\n" | ||
188 | " kill BUNDLEID kill app with BUNDLEID\n" | ||
189 | "\n" | ||
190 | "The following OPTIONS are accepted:\n" | ||
191 | " -u, --udid UDID target specific device by UDID\n" | ||
192 | " -n, --network connect to network device\n" | ||
193 | " --detach detach from app after launch, keeping it running\n" | ||
194 | " -e, --env NAME=VALUE set environment variable NAME to VALUE\n" | ||
195 | " -d, --debug enable communication debugging\n" | ||
196 | " -h, --help prints usage information\n" | ||
197 | " -v, --version prints version information\n" | ||
198 | "\n" | ||
199 | "Homepage: <" PACKAGE_URL ">\n" | ||
200 | "Bug Reports: <" PACKAGE_BUGREPORT ">\n" | ||
201 | ); | ||
202 | } | ||
203 | |||
204 | int main(int argc, char *argv[]) | ||
205 | { | ||
206 | int res = -1; | ||
207 | idevice_t device = NULL; | ||
208 | idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; | ||
209 | instproxy_client_t instproxy_client = NULL; | ||
210 | debugserver_client_t debugserver_client = NULL; | ||
211 | int i; | ||
212 | int cmd = CMD_NONE; | ||
213 | const char* udid = NULL; | ||
214 | int use_network = 0; | ||
215 | int detach_after_start = 0; | ||
216 | const char* bundle_identifier = NULL; | ||
217 | char* path = NULL; | ||
218 | char* working_directory = NULL; | ||
219 | char **newlist = NULL; | ||
220 | char** environment = NULL; | ||
221 | int environment_index = 0; | ||
222 | int environment_count = 0; | ||
223 | char* response = NULL; | ||
224 | debugserver_command_t command = NULL; | ||
225 | debugserver_error_t dres = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
226 | |||
227 | int c = 0; | ||
228 | const struct option longopts[] = { | ||
229 | { "debug", no_argument, NULL, 'd' }, | ||
230 | { "help", no_argument, NULL, 'h' }, | ||
231 | { "udid", required_argument, NULL, 'u' }, | ||
232 | { "network", no_argument, NULL, 'n' }, | ||
233 | { "detach", no_argument, NULL, 1 }, | ||
234 | { "env", required_argument, NULL, 'e' }, | ||
235 | { "version", no_argument, NULL, 'v' }, | ||
236 | { NULL, 0, NULL, 0 } | ||
237 | }; | ||
238 | |||
239 | /* map signals */ | ||
240 | signal(SIGINT, on_signal); | ||
241 | signal(SIGTERM, on_signal); | ||
242 | #ifndef WIN32 | ||
243 | signal(SIGQUIT, on_signal); | ||
244 | signal(SIGPIPE, SIG_IGN); | ||
245 | #endif | ||
246 | |||
247 | while ((c = getopt_long(argc, argv, "dhu:ne:v", longopts, NULL)) != -1) { | ||
248 | switch (c) { | ||
249 | case 'd': | ||
250 | debug_level++; | ||
251 | if (debug_level > 1) { | ||
252 | idevice_set_debug_level(debug_level-1); | ||
253 | } | ||
254 | break; | ||
255 | case 'u': | ||
256 | if (!*optarg) { | ||
257 | fprintf(stderr, "ERROR: UDID must not be empty!\n"); | ||
258 | print_usage(argc, argv, 1); | ||
259 | return 2; | ||
260 | } | ||
261 | udid = optarg; | ||
262 | break; | ||
263 | case 'n': | ||
264 | use_network = 1; | ||
265 | break; | ||
266 | case 1: | ||
267 | detach_after_start = 1; | ||
268 | break; | ||
269 | case 'e': | ||
270 | if (!*optarg || strchr(optarg, '=') == NULL) { | ||
271 | fprintf(stderr, "ERROR: environment variables need to be specified as -e KEY=VALUE\n"); | ||
272 | print_usage(argc, argv, 1); | ||
273 | res = 2; | ||
274 | goto cleanup; | ||
275 | } | ||
276 | /* add environment variable */ | ||
277 | if (!newlist) | ||
278 | newlist = malloc((environment_count + 1) * sizeof(char*)); | ||
279 | else | ||
280 | newlist = realloc(environment, (environment_count + 1) * sizeof(char*)); | ||
281 | newlist[environment_count++] = strdup(optarg); | ||
282 | environment = newlist; | ||
283 | break; | ||
284 | case 'h': | ||
285 | print_usage(argc, argv, 0); | ||
286 | res = 0; | ||
287 | goto cleanup; | ||
288 | break; | ||
289 | case 'v': | ||
290 | printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); | ||
291 | res = 0; | ||
292 | goto cleanup; | ||
293 | break; | ||
294 | default: | ||
295 | print_usage(argc, argv, 1); | ||
296 | res = 2; | ||
297 | goto cleanup; | ||
298 | break; | ||
299 | } | ||
300 | } | ||
301 | argc -= optind; | ||
302 | argv += optind; | ||
303 | |||
304 | if (argc < 1) { | ||
305 | fprintf(stderr, "ERROR: Missing command.\n"); | ||
306 | print_usage(argc+optind, argv-optind, 1); | ||
307 | return 2; | ||
308 | } | ||
309 | |||
310 | if (!strcmp(argv[0], "run")) { | ||
311 | cmd = CMD_RUN; | ||
312 | if (argc < 2) { | ||
313 | /* make sure at least the bundle identifier was provided */ | ||
314 | fprintf(stderr, "ERROR: Please supply the bundle identifier of the app to run.\n"); | ||
315 | print_usage(argc+optind, argv-optind, 1); | ||
316 | res = 2; | ||
317 | goto cleanup; | ||
318 | } | ||
319 | /* read bundle identifier */ | ||
320 | bundle_identifier = argv[1]; | ||
321 | i = 1; | ||
322 | } else if (!strcmp(argv[0], "kill")) { | ||
323 | cmd = CMD_KILL; | ||
324 | if (argc < 2) { | ||
325 | /* make sure at least the bundle identifier was provided */ | ||
326 | fprintf(stderr, "ERROR: Please supply the bundle identifier of the app to run.\n"); | ||
327 | print_usage(argc+optind, argv-optind, 1); | ||
328 | res = 2; | ||
329 | goto cleanup; | ||
330 | } | ||
331 | /* read bundle identifier */ | ||
332 | bundle_identifier = argv[1]; | ||
333 | i = 1; | ||
334 | } | ||
335 | |||
336 | /* verify options */ | ||
337 | if (cmd == CMD_NONE) { | ||
338 | fprintf(stderr, "ERROR: Unsupported command specified.\n"); | ||
339 | print_usage(argc+optind, argv-optind, 1); | ||
340 | res = 2; | ||
341 | goto cleanup; | ||
342 | } | ||
343 | |||
344 | if (environment) { | ||
345 | newlist = realloc(environment, (environment_count + 1) * sizeof(char*)); | ||
346 | newlist[environment_count] = NULL; | ||
347 | environment = newlist; | ||
348 | } | ||
349 | |||
350 | /* connect to the device */ | ||
351 | ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); | ||
352 | if (ret != IDEVICE_E_SUCCESS) { | ||
353 | if (udid) { | ||
354 | printf("No device found with udid %s.\n", udid); | ||
355 | } else { | ||
356 | printf("No device found.\n"); | ||
357 | } | ||
358 | goto cleanup; | ||
359 | } | ||
360 | |||
361 | /* get the path to the app and it's working directory */ | ||
362 | if (instproxy_client_start_service(device, &instproxy_client, TOOL_NAME) != INSTPROXY_E_SUCCESS) { | ||
363 | fprintf(stderr, "Could not start installation proxy service.\n"); | ||
364 | goto cleanup; | ||
365 | } | ||
366 | |||
367 | instproxy_client_get_path_for_bundle_identifier(instproxy_client, bundle_identifier, &path); | ||
368 | if (!path) { | ||
369 | fprintf(stderr, "Invalid bundle identifier: %s\n", bundle_identifier); | ||
370 | goto cleanup; | ||
371 | } | ||
372 | |||
373 | plist_t container = NULL; | ||
374 | instproxy_client_get_object_by_key_from_info_dictionary_for_bundle_identifier(instproxy_client, bundle_identifier, "Container", &container); | ||
375 | instproxy_client_free(instproxy_client); | ||
376 | instproxy_client = NULL; | ||
377 | |||
378 | if (container && (plist_get_node_type(container) == PLIST_STRING)) { | ||
379 | plist_get_string_val(container, &working_directory); | ||
380 | log_debug("working_directory: %s\n", working_directory); | ||
381 | plist_free(container); | ||
382 | } else { | ||
383 | plist_free(container); | ||
384 | fprintf(stderr, "Could not determine container path for bundle identifier %s.\n", bundle_identifier); | ||
385 | goto cleanup; | ||
386 | } | ||
387 | |||
388 | /* start and connect to debugserver */ | ||
389 | if (debugserver_client_start_service(device, &debugserver_client, TOOL_NAME) != DEBUGSERVER_E_SUCCESS) { | ||
390 | fprintf(stderr, | ||
391 | "Could not start com.apple.debugserver!\n" | ||
392 | "Please make sure to mount the developer disk image first:\n" | ||
393 | " 1) Get the iOS version from `ideviceinfo -k ProductVersion`.\n" | ||
394 | " 2) Find the matching iPhoneOS DeveloperDiskImage.dmg files.\n" | ||
395 | " 3) Run `ideviceimagemounter` with the above path.\n"); | ||
396 | goto cleanup; | ||
397 | } | ||
398 | |||
399 | /* set receive params */ | ||
400 | if (debugserver_client_set_receive_params(debugserver_client, cancel_receive, 250) != DEBUGSERVER_E_SUCCESS) { | ||
401 | fprintf(stderr, "Error in debugserver_client_set_receive_params\n"); | ||
402 | goto cleanup; | ||
403 | } | ||
404 | |||
405 | /* enable logging for the session in debug mode */ | ||
406 | if (debug_level) { | ||
407 | log_debug("Setting logging bitmask..."); | ||
408 | debugserver_command_new("QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE|LOG_RNB_PACKETS;", 0, NULL, &command); | ||
409 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
410 | debugserver_command_free(command); | ||
411 | command = NULL; | ||
412 | if (response) { | ||
413 | if (strncmp(response, "OK", 2) != 0) { | ||
414 | debugserver_client_handle_response(debugserver_client, &response, NULL); | ||
415 | goto cleanup; | ||
416 | } | ||
417 | free(response); | ||
418 | response = NULL; | ||
419 | } | ||
420 | } | ||
421 | |||
422 | /* set maximum packet size */ | ||
423 | log_debug("Setting maximum packet size..."); | ||
424 | char* packet_size[2] = { (char*)"1024", NULL}; | ||
425 | debugserver_command_new("QSetMaxPacketSize:", 1, packet_size, &command); | ||
426 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
427 | debugserver_command_free(command); | ||
428 | command = NULL; | ||
429 | if (response) { | ||
430 | if (strncmp(response, "OK", 2) != 0) { | ||
431 | debugserver_client_handle_response(debugserver_client, &response, NULL); | ||
432 | goto cleanup; | ||
433 | } | ||
434 | free(response); | ||
435 | response = NULL; | ||
436 | } | ||
437 | |||
438 | /* set working directory */ | ||
439 | log_debug("Setting working directory..."); | ||
440 | char* working_dir[2] = {working_directory, NULL}; | ||
441 | debugserver_command_new("QSetWorkingDir:", 1, working_dir, &command); | ||
442 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
443 | debugserver_command_free(command); | ||
444 | command = NULL; | ||
445 | if (response) { | ||
446 | if (strncmp(response, "OK", 2) != 0) { | ||
447 | debugserver_client_handle_response(debugserver_client, &response, NULL); | ||
448 | goto cleanup; | ||
449 | } | ||
450 | free(response); | ||
451 | response = NULL; | ||
452 | } | ||
453 | |||
454 | /* set environment */ | ||
455 | if (environment) { | ||
456 | log_debug("Setting environment..."); | ||
457 | for (environment_index = 0; environment_index < environment_count; environment_index++) { | ||
458 | log_debug("setting environment variable: %s", environment[environment_index]); | ||
459 | debugserver_client_set_environment_hex_encoded(debugserver_client, environment[environment_index], NULL); | ||
460 | } | ||
461 | } | ||
462 | |||
463 | /* set arguments and run app */ | ||
464 | log_debug("Setting argv..."); | ||
465 | i++; /* i is the offset of the bundle identifier, thus skip it */ | ||
466 | int app_argc = (argc - i + 2); | ||
467 | char **app_argv = (char**)malloc(sizeof(char*) * app_argc); | ||
468 | app_argv[0] = path; | ||
469 | log_debug("app_argv[%d] = %s", 0, app_argv[0]); | ||
470 | app_argc = 1; | ||
471 | while (i < argc && argv && argv[i]) { | ||
472 | log_debug("app_argv[%d] = %s", app_argc, argv[i]); | ||
473 | app_argv[app_argc++] = argv[i]; | ||
474 | i++; | ||
475 | } | ||
476 | app_argv[app_argc] = NULL; | ||
477 | debugserver_client_set_argv(debugserver_client, app_argc, app_argv, NULL); | ||
478 | free(app_argv); | ||
479 | |||
480 | /* check if launch succeeded */ | ||
481 | log_debug("Checking if launch succeeded..."); | ||
482 | debugserver_command_new("qLaunchSuccess", 0, NULL, &command); | ||
483 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
484 | debugserver_command_free(command); | ||
485 | command = NULL; | ||
486 | if (response) { | ||
487 | if (strncmp(response, "OK", 2) != 0) { | ||
488 | debugserver_client_handle_response(debugserver_client, &response, NULL); | ||
489 | goto cleanup; | ||
490 | } | ||
491 | free(response); | ||
492 | response = NULL; | ||
493 | } | ||
494 | |||
495 | if (cmd == CMD_KILL) { | ||
496 | debugserver_command_new("k", 0, NULL, &command); | ||
497 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
498 | debugserver_command_free(command); | ||
499 | command = NULL; | ||
500 | goto cleanup; | ||
501 | } else | ||
502 | if (cmd == CMD_RUN) { | ||
503 | if (detach_after_start) { | ||
504 | log_debug("Detaching from app"); | ||
505 | debugserver_command_new("D", 0, NULL, &command); | ||
506 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
507 | debugserver_command_free(command); | ||
508 | command = NULL; | ||
509 | |||
510 | res = (dres == DEBUGSERVER_E_SUCCESS) ? 0: -1; | ||
511 | goto cleanup; | ||
512 | } | ||
513 | |||
514 | /* set thread */ | ||
515 | log_debug("Setting thread..."); | ||
516 | debugserver_command_new("Hc0", 0, NULL, &command); | ||
517 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
518 | debugserver_command_free(command); | ||
519 | command = NULL; | ||
520 | if (response) { | ||
521 | if (strncmp(response, "OK", 2) != 0) { | ||
522 | debugserver_client_handle_response(debugserver_client, &response, NULL); | ||
523 | goto cleanup; | ||
524 | } | ||
525 | free(response); | ||
526 | response = NULL; | ||
527 | } | ||
528 | |||
529 | /* continue running process */ | ||
530 | log_debug("Continue running process..."); | ||
531 | debugserver_command_new("c", 0, NULL, &command); | ||
532 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
533 | debugserver_command_free(command); | ||
534 | command = NULL; | ||
535 | log_debug("Continue response: %s", response); | ||
536 | |||
537 | /* main loop which is parsing/handling packets during the run */ | ||
538 | log_debug("Entering run loop..."); | ||
539 | while (!quit_flag) { | ||
540 | if (dres != DEBUGSERVER_E_SUCCESS) { | ||
541 | log_debug("failed to receive response; error %d", dres); | ||
542 | break; | ||
543 | } | ||
544 | |||
545 | if (response) { | ||
546 | log_debug("response: %s", response); | ||
547 | if (strncmp(response, "OK", 2) != 0) { | ||
548 | dres = debugserver_client_handle_response(debugserver_client, &response, &res); | ||
549 | if (dres != DEBUGSERVER_E_SUCCESS) { | ||
550 | log_debug("failed to process response; error %d; %s", dres, response); | ||
551 | break; | ||
552 | } | ||
553 | } | ||
554 | } | ||
555 | if (res >= 0) { | ||
556 | goto cleanup; | ||
557 | } | ||
558 | |||
559 | dres = debugserver_client_receive_response(debugserver_client, &response, NULL); | ||
560 | } | ||
561 | |||
562 | /* ignore quit_flag after this point */ | ||
563 | if (debugserver_client_set_receive_params(debugserver_client, NULL, 5000) != DEBUGSERVER_E_SUCCESS) { | ||
564 | fprintf(stderr, "Error in debugserver_client_set_receive_params\n"); | ||
565 | goto cleanup; | ||
566 | } | ||
567 | |||
568 | /* interrupt execution */ | ||
569 | debugserver_command_new("\x03", 0, NULL, &command); | ||
570 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
571 | debugserver_command_free(command); | ||
572 | command = NULL; | ||
573 | if (response) { | ||
574 | if (strncmp(response, "OK", 2) != 0) { | ||
575 | debugserver_client_handle_response(debugserver_client, &response, NULL); | ||
576 | } | ||
577 | free(response); | ||
578 | response = NULL; | ||
579 | } | ||
580 | |||
581 | /* kill process after we finished */ | ||
582 | log_debug("Killing process..."); | ||
583 | debugserver_command_new("k", 0, NULL, &command); | ||
584 | dres = debugserver_client_send_command(debugserver_client, command, &response, NULL); | ||
585 | debugserver_command_free(command); | ||
586 | command = NULL; | ||
587 | if (response) { | ||
588 | if (strncmp(response, "OK", 2) != 0) { | ||
589 | debugserver_client_handle_response(debugserver_client, &response, NULL); | ||
590 | } | ||
591 | free(response); | ||
592 | response = NULL; | ||
593 | } | ||
594 | |||
595 | if (res < 0) { | ||
596 | res = (dres == DEBUGSERVER_E_SUCCESS) ? 0: -1; | ||
597 | } | ||
598 | } | ||
599 | |||
600 | cleanup: | ||
601 | /* cleanup the house */ | ||
602 | if (environment) { | ||
603 | for (environment_index = 0; environment_index < environment_count; environment_index++) { | ||
604 | free(environment[environment_index]); | ||
605 | } | ||
606 | free(environment); | ||
607 | } | ||
608 | |||
609 | if (working_directory) | ||
610 | free(working_directory); | ||
611 | |||
612 | if (path) | ||
613 | free(path); | ||
614 | |||
615 | if (response) | ||
616 | free(response); | ||
617 | |||
618 | if (debugserver_client) | ||
619 | debugserver_client_free(debugserver_client); | ||
620 | |||
621 | if (device) | ||
622 | idevice_free(device); | ||
623 | |||
624 | return res; | ||
625 | } | ||