| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- /*
- * idevicecrashreport.c
- * Simple utility to move crash reports from a device to a local directory.
- *
- * Copyright (c) 2014 Martin Szulecki. All Rights Reserved.
- * Copyright (c) 2014 Nikias Bassen. All Rights Reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "common/utils.h"
- #include <libimobiledevice/afc.h>
- #include <libimobiledevice/lockdown.h>
- #include <libimobiledevice/libimobiledevice.h>
- #include <plist/plist.h>
- #ifdef WIN32
- #include <windows.h>
- #define S_IFLNK S_IFREG
- #define S_IFSOCK S_IFREG
- #endif
- const char* target_directory = NULL;
- static int extract_raw_crash_reports = 0;
- static int keep_crash_reports = 0;
- static int file_exists(const char* path)
- {
- struct stat tst;
- #ifdef WIN32
- return (stat(path, &tst) == 0);
- #else
- return (lstat(path, &tst) == 0);
- #endif
- }
- static int extract_raw_crash_report(const char* filename) {
- int res = 0;
- plist_t report = NULL;
- char* raw = NULL;
- char* raw_filename = strdup(filename);
- /* create filename with '.crash' extension */
- char* p = strrchr(raw_filename, '.');
- if ((p == NULL) || (strcmp(p, ".plist") != 0)) {
- free(raw_filename);
- return res;
- }
- strcpy(p, ".crash");
- /* read plist crash report */
- if (plist_read_from_filename(&report, filename)) {
- plist_t description_node = plist_dict_get_item(report, "description");
- if (description_node && plist_get_node_type(description_node) == PLIST_STRING) {
- plist_get_string_val(description_node, &raw);
- if (raw != NULL) {
- /* write file */
- buffer_write_to_filename(raw_filename, raw, strlen(raw));
- free(raw);
- res = 1;
- }
- }
- }
- if (report)
- plist_free(report);
- if (raw_filename)
- free(raw_filename);
- return res;
- }
- static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char* device_directory, const char* host_directory)
- {
- afc_error_t afc_error;
- int k;
- int res = -1;
- int crash_report_count = 0;
- uint64_t handle;
- char source_filename[512];
- char target_filename[512];
- if (!afc)
- return res;
- char** list = NULL;
- afc_error = afc_read_directory(afc, device_directory, &list);
- if (afc_error != AFC_E_SUCCESS) {
- fprintf(stderr, "ERROR: Could not read device directory '%s'\n", device_directory);
- return res;
- }
- /* ensure we have a trailing slash */
- strcpy(source_filename, device_directory);
- if (source_filename[strlen(source_filename)-1] != '/') {
- strcat(source_filename, "/");
- }
- int device_directory_length = strlen(source_filename);
- /* ensure we have a trailing slash */
- strcpy(target_filename, host_directory);
- if (target_filename[strlen(target_filename)-1] != '/') {
- strcat(target_filename, "/");
- }
- int host_directory_length = strlen(target_filename);
- /* loop over file entries */
- for (k = 0; list[k]; k++) {
- if (!strcmp(list[k], ".") || !strcmp(list[k], "..")) {
- continue;
- }
- char **fileinfo = NULL;
- struct stat stbuf;
- stbuf.st_size = 0;
- /* assemble absolute source filename */
- strcpy(((char*)source_filename) + device_directory_length, list[k]);
- /* assemble absolute target filename */
- char* p = strrchr(list[k], '.');
- if (p != NULL && !strncmp(p, ".synced", 7)) {
- /* make sure to strip ".synced" extension as seen on iOS 5 */
- strncpy(((char*)target_filename) + host_directory_length, list[k], strlen(list[k]) - 7);
- } else {
- strcpy(((char*)target_filename) + host_directory_length, list[k]);
- }
- /* get file information */
- afc_get_file_info(afc, source_filename, &fileinfo);
- if (!fileinfo) {
- printf("Failed to read information for '%s'. Skipping...\n", source_filename);
- continue;
- }
- /* parse file information */
- int i;
- for (i = 0; fileinfo[i]; i+=2) {
- if (!strcmp(fileinfo[i], "st_size")) {
- stbuf.st_size = atoll(fileinfo[i+1]);
- } else if (!strcmp(fileinfo[i], "st_ifmt")) {
- if (!strcmp(fileinfo[i+1], "S_IFREG")) {
- stbuf.st_mode = S_IFREG;
- } else if (!strcmp(fileinfo[i+1], "S_IFDIR")) {
- stbuf.st_mode = S_IFDIR;
- } else if (!strcmp(fileinfo[i+1], "S_IFLNK")) {
- stbuf.st_mode = S_IFLNK;
- } else if (!strcmp(fileinfo[i+1], "S_IFBLK")) {
- stbuf.st_mode = S_IFBLK;
- } else if (!strcmp(fileinfo[i+1], "S_IFCHR")) {
- stbuf.st_mode = S_IFCHR;
- } else if (!strcmp(fileinfo[i+1], "S_IFIFO")) {
- stbuf.st_mode = S_IFIFO;
- } else if (!strcmp(fileinfo[i+1], "S_IFSOCK")) {
- stbuf.st_mode = S_IFSOCK;
- }
- } else if (!strcmp(fileinfo[i], "st_nlink")) {
- stbuf.st_nlink = atoi(fileinfo[i+1]);
- } else if (!strcmp(fileinfo[i], "st_mtime")) {
- stbuf.st_mtime = (time_t)(atoll(fileinfo[i+1]) / 1000000000);
- } else if (!strcmp(fileinfo[i], "LinkTarget")) {
- /* report latest crash report filename */
- printf("Link: %s\n", (char*)target_filename + strlen(target_directory));
- /* remove any previous symlink */
- if (file_exists(target_filename)) {
- remove(target_filename);
- }
- #ifndef WIN32
- /* use relative filename */
- char* b = strrchr(fileinfo[i+1], '/');
- if (b == NULL) {
- b = fileinfo[i+1];
- } else {
- b++;
- }
- /* create a symlink pointing to latest log */
- if (symlink(b, target_filename) < 0) {
- fprintf(stderr, "Can't create symlink to %s\n", b);
- }
- #endif
- if (!keep_crash_reports)
- afc_remove_path(afc, source_filename);
- res = 0;
- }
- }
- /* free file information */
- afc_dictionary_free(fileinfo);
- /* recurse into child directories */
- if (S_ISDIR(stbuf.st_mode)) {
- #ifdef WIN32
- mkdir(target_filename);
- #else
- mkdir(target_filename, 0755);
- #endif
- res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename);
- /* remove directory from device */
- if (!keep_crash_reports)
- afc_remove_path(afc, source_filename);
- } else if (S_ISREG(stbuf.st_mode)) {
- /* copy file to host */
- afc_error = afc_file_open(afc, source_filename, AFC_FOPEN_RDONLY, &handle);
- if(afc_error != AFC_E_SUCCESS) {
- if (afc_error == AFC_E_OBJECT_NOT_FOUND) {
- continue;
- }
- fprintf(stderr, "Unable to open device file '%s' (%d). Skipping...\n", source_filename, afc_error);
- continue;
- }
- FILE* output = fopen(target_filename, "wb");
- if(output == NULL) {
- fprintf(stderr, "Unable to open local file '%s'. Skipping...\n", target_filename);
- afc_file_close(afc, handle);
- continue;
- }
- printf("%s: %s\n", (keep_crash_reports ? "Copy": "Move") , (char*)target_filename + strlen(target_directory));
- uint32_t bytes_read = 0;
- uint32_t bytes_total = 0;
- unsigned char data[0x1000];
- afc_error = afc_file_read(afc, handle, (char*)data, 0x1000, &bytes_read);
- while(afc_error == AFC_E_SUCCESS && bytes_read > 0) {
- fwrite(data, 1, bytes_read, output);
- bytes_total += bytes_read;
- afc_error = afc_file_read(afc, handle, (char*)data, 0x1000, &bytes_read);
- }
- afc_file_close(afc, handle);
- fclose(output);
- if ((uint32_t)stbuf.st_size != bytes_total) {
- fprintf(stderr, "File size mismatch. Skipping...\n");
- continue;
- }
- /* remove file from device */
- if (!keep_crash_reports) {
- afc_remove_path(afc, source_filename);
- }
- /* extract raw crash information into separate '.crash' file */
- if (extract_raw_crash_reports) {
- extract_raw_crash_report(target_filename);
- }
- crash_report_count++;
- res = 0;
- }
- }
- afc_dictionary_free(list);
- /* no reports, no error */
- if (crash_report_count == 0)
- res = 0;
- return res;
- }
- static void print_usage(int argc, char **argv)
- {
- char *name = NULL;
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
- printf("Move crash reports from device to a local DIRECTORY.\n\n");
- printf(" -e, --extract\t\textract raw crash report into separate '.crash' file\n");
- printf(" -k, --keep\t\tcopy but do not remove crash reports from device\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by its 40-digit device UDID\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <http://libimobiledevice.org>\n");
- }
- int main(int argc, char* argv[]) {
- idevice_t device = NULL;
- lockdownd_client_t lockdownd = NULL;
- afc_client_t afc = NULL;
- idevice_error_t device_error = IDEVICE_E_SUCCESS;
- lockdownd_error_t lockdownd_error = LOCKDOWN_E_SUCCESS;
- afc_error_t afc_error = AFC_E_SUCCESS;
- int i;
- const char* udid = NULL;
- /* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
- idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || (strlen(argv[i]) != 40)) {
- print_usage(argc, argv);
- return 0;
- }
- udid = argv[i];
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
- return 0;
- }
- else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--extract")) {
- extract_raw_crash_reports = 1;
- continue;
- }
- else if (!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keep")) {
- keep_crash_reports = 1;
- continue;
- }
- else if (target_directory == NULL) {
- target_directory = argv[i];
- continue;
- }
- else {
- print_usage(argc, argv);
- return 0;
- }
- }
- /* ensure a target directory was supplied */
- if (!target_directory) {
- print_usage(argc, argv);
- return 0;
- }
- /* check if target directory exists */
- if (!file_exists(target_directory)) {
- fprintf(stderr, "ERROR: Directory '%s' does not exist.\n", target_directory);
- print_usage(argc, argv);
- return 0;
- }
- device_error = idevice_new(&device, udid);
- if (device_error != IDEVICE_E_SUCCESS) {
- if (udid) {
- printf("No device found with udid %s, is it plugged in?\n", udid);
- } else {
- printf("No device found, is it plugged in?\n");
- }
- return -1;
- }
- lockdownd_error = lockdownd_client_new_with_handshake(device, &lockdownd, "idevicecrashreport");
- if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
- fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lockdownd_error);
- idevice_free(device);
- return -1;
- }
- /* start crash log mover service */
- lockdownd_service_descriptor_t service = NULL;
- lockdownd_error = lockdownd_start_service(lockdownd, "com.apple.crashreportmover", &service);
- if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
- lockdownd_client_free(lockdownd);
- idevice_free(device);
- return -1;
- }
- /* trigger move operation on device */
- idevice_connection_t connection = NULL;
- device_error = idevice_connect(device, service->port, &connection);
- if(device_error != IDEVICE_E_SUCCESS) {
- lockdownd_client_free(lockdownd);
- idevice_free(device);
- return -1;
- }
- /* read "ping" message which indicates the crash logs have been moved to a safe harbor */
- char *ping = malloc(4);
- int attempts = 0;
- while ((strncmp(ping, "ping", 4) != 0) && (attempts > 10)) {
- uint32_t bytes = 0;
- device_error = idevice_connection_receive_timeout(connection, ping, 4, &bytes, 2000);
- if ((bytes == 0) && (device_error == IDEVICE_E_SUCCESS)) {
- attempts++;
- continue;
- } else if (device_error < 0) {
- fprintf(stderr, "ERROR: Crash logs could not be moved. Connection interrupted.\n");
- break;
- }
- }
- idevice_disconnect(connection);
- free(ping);
- if (service) {
- lockdownd_service_descriptor_free(service);
- service = NULL;
- }
- if (device_error != IDEVICE_E_SUCCESS || attempts > 10) {
- fprintf(stderr, "ERROR: Failed to receive ping message from crash report mover.\n");
- lockdownd_client_free(lockdownd);
- idevice_free(device);
- return -1;
- }
- lockdownd_error = lockdownd_start_service(lockdownd, "com.apple.crashreportcopymobile", &service);
- if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
- lockdownd_client_free(lockdownd);
- idevice_free(device);
- return -1;
- }
- lockdownd_client_free(lockdownd);
- afc = NULL;
- afc_error = afc_client_new(device, service, &afc);
- if(afc_error != AFC_E_SUCCESS) {
- lockdownd_client_free(lockdownd);
- idevice_free(device);
- return -1;
- }
- if (service) {
- lockdownd_service_descriptor_free(service);
- service = NULL;
- }
- /* recursively copy crash reports from the device to a local directory */
- if (afc_client_copy_and_remove_crash_reports(afc, ".", target_directory) < 0) {
- fprintf(stderr, "ERROR: Failed to get crash reports from device.\n");
- afc_client_free(afc);
- idevice_free(device);
- return -1;
- }
- printf("Done.\n");
- afc_client_free(afc);
- idevice_free(device);
- return 0;
- }
|