diff options
| -rw-r--r-- | docs/idevicesyslog.1 | 34 | ||||
| -rw-r--r-- | tools/idevicesyslog.c | 174 |
2 files changed, 189 insertions, 19 deletions
diff --git a/docs/idevicesyslog.1 b/docs/idevicesyslog.1 index 0345bff..cb91193 100644 --- a/docs/idevicesyslog.1 +++ b/docs/idevicesyslog.1 | |||
| @@ -42,17 +42,41 @@ Force writing colored output, e.g. when using \f[B]\-\-output\f[]. | |||
| 42 | .B \-\-syslog_relay | 42 | .B \-\-syslog_relay |
| 43 | Use old syslog_relay service instead of os_trace_relay (iOS 9+). | 43 | Use old syslog_relay service instead of os_trace_relay (iOS 9+). |
| 44 | 44 | ||
| 45 | .SH COMMANDS | ||
| 46 | .TP | ||
| 47 | .B pidlist | ||
| 48 | Print a list with PID and name of all processes currently running on the device. | ||
| 49 | .TP | ||
| 50 | .B archive PATH | ||
| 51 | Request a logarchive from the device. It will be written in tar format to PATH. To pipe to another process use \- as PATH. | ||
| 52 | Below are some options to restrict the log message data. | ||
| 53 | |||
| 54 | In order to view the logarchive in a compatible log viewer, you can pipe the output data to \f[B]tar\f[] and have it extract into a new directory: | ||
| 55 | |||
| 56 | \f[B]mkdir test.logarchive && tools/idevicesyslog archive - |tar -C test.logarchive -xv\f[] | ||
| 57 | |||
| 58 | This will also print the filenames while they are extracted. | ||
| 59 | .TP | ||
| 60 | Further options for \f[B]archive\f[]: | ||
| 61 | .TP | ||
| 62 | .B \-\-start\-time VALUE | ||
| 63 | Start time of the log data as UNIX timestamp. Earlier messages will be dropped. | ||
| 64 | .TP | ||
| 65 | .B \-\-age\-limit VALUE | ||
| 66 | Maximum age of the log data, supposedly number of days. | ||
| 67 | .TP | ||
| 68 | .B \-\-size\-limit VALUE | ||
| 69 | Limit the size of the archive. The unit is currently unknown, so feel free to experiment. | ||
| 70 | .TP | ||
| 71 | Keep in mind that the device usually only has a backlog of a few minutes so the options might not have the desired effect. This is not a bug. | ||
| 72 | |||
| 45 | .SH FILTER OPTIONS | 73 | .SH FILTER OPTIONS |
| 46 | .TP | 74 | .TP |
| 47 | .B \-m, \-\-match STRING | 75 | .B \-m, \-\-match STRING |
| 48 | only print messages that contain STRING | 76 | only print messages that contain STRING |
| 49 | |||
| 50 | .SH FILTER OPTIONS | ||
| 51 | .TP | 77 | .TP |
| 52 | .B \-M, \-\-unmatch STRING | 78 | .B \-M, \-\-unmatch STRING |
| 53 | print messages that not contain STRING | 79 | print messages that do not contain STRING |
| 54 | |||
| 55 | This option will set a filter to only printed log messages that contain the given string. | ||
| 56 | .TP | 80 | .TP |
| 57 | .B \-t, \-\-trigger STRING | 81 | .B \-t, \-\-trigger STRING |
| 58 | start logging when matching STRING | 82 | start logging when matching STRING |
diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c index bd73f88..8d7e33b 100644 --- a/tools/idevicesyslog.c +++ b/tools/idevicesyslog.c | |||
| @@ -78,6 +78,10 @@ static const char QUIET_FILTER[] = "CircleJoinRequested|CommCenter|HeuristicInte | |||
| 78 | 78 | ||
| 79 | static int use_network = 0; | 79 | static int use_network = 0; |
| 80 | 80 | ||
| 81 | static long long start_time = -1; | ||
| 82 | static long long size_limit = -1; | ||
| 83 | static long long age_limit = -1; | ||
| 84 | |||
| 81 | static char *line = NULL; | 85 | static char *line = NULL; |
| 82 | static int line_buffer_size = 0; | 86 | static int line_buffer_size = 0; |
| 83 | static int lp = 0; | 87 | static int lp = 0; |
| @@ -538,7 +542,7 @@ static void ostrace_syslog_callback(const void* buf, size_t len, void* user_data | |||
| 538 | } | 542 | } |
| 539 | } | 543 | } |
| 540 | 544 | ||
| 541 | static int start_logging(void) | 545 | static int connect_service(int ostrace_required) |
| 542 | { | 546 | { |
| 543 | idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); | 547 | idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); |
| 544 | if (ret != IDEVICE_E_SUCCESS) { | 548 | if (ret != IDEVICE_E_SUCCESS) { |
| @@ -562,6 +566,13 @@ static int start_logging(void) | |||
| 562 | service_name = SYSLOG_RELAY_SERVICE_NAME; | 566 | service_name = SYSLOG_RELAY_SERVICE_NAME; |
| 563 | use_ostrace = 0; | 567 | use_ostrace = 0; |
| 564 | } | 568 | } |
| 569 | if (ostrace_required && !use_ostrace) { | ||
| 570 | fprintf(stderr, "ERROR: This operation requires iOS 9 or later.\n"); | ||
| 571 | lockdownd_client_free(lockdown); | ||
| 572 | idevice_free(device); | ||
| 573 | device = NULL; | ||
| 574 | return -1; | ||
| 575 | } | ||
| 565 | 576 | ||
| 566 | /* start syslog_relay/os_trace_relay service */ | 577 | /* start syslog_relay/os_trace_relay service */ |
| 567 | lerr = lockdownd_start_service(lockdown, service_name, &svc); | 578 | lerr = lockdownd_start_service(lockdown, service_name, &svc); |
| @@ -594,16 +605,6 @@ static int start_logging(void) | |||
| 594 | device = NULL; | 605 | device = NULL; |
| 595 | return -1; | 606 | return -1; |
| 596 | } | 607 | } |
| 597 | |||
| 598 | serr = ostrace_start_activity(ostrace, NULL, ostrace_syslog_callback, NULL); | ||
| 599 | if (serr != OSTRACE_E_SUCCESS) { | ||
| 600 | fprintf(stderr, "ERROR: Unable to start capturing syslog.\n"); | ||
| 601 | ostrace_client_free(ostrace); | ||
| 602 | ostrace = NULL; | ||
| 603 | idevice_free(device); | ||
| 604 | device = NULL; | ||
| 605 | return -1; | ||
| 606 | } | ||
| 607 | } else { | 608 | } else { |
| 608 | /* connect to syslog_relay service */ | 609 | /* connect to syslog_relay service */ |
| 609 | syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR; | 610 | syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR; |
| @@ -615,9 +616,29 @@ static int start_logging(void) | |||
| 615 | device = NULL; | 616 | device = NULL; |
| 616 | return -1; | 617 | return -1; |
| 617 | } | 618 | } |
| 619 | } | ||
| 620 | return 0; | ||
| 621 | } | ||
| 618 | 622 | ||
| 619 | /* start capturing syslog */ | 623 | static int start_logging(void) |
| 620 | serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL); | 624 | { |
| 625 | if (connect_service(0) < 0) { | ||
| 626 | return -1; | ||
| 627 | } | ||
| 628 | |||
| 629 | /* start capturing syslog */ | ||
| 630 | if (ostrace) { | ||
| 631 | ostrace_error_t serr = ostrace_start_activity(ostrace, NULL, ostrace_syslog_callback, NULL); | ||
| 632 | if (serr != OSTRACE_E_SUCCESS) { | ||
| 633 | fprintf(stderr, "ERROR: Unable to start capturing syslog.\n"); | ||
| 634 | ostrace_client_free(ostrace); | ||
| 635 | ostrace = NULL; | ||
| 636 | idevice_free(device); | ||
| 637 | device = NULL; | ||
| 638 | return -1; | ||
| 639 | } | ||
| 640 | } else if (syslog) { | ||
| 641 | syslog_relay_error_t serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL); | ||
| 621 | if (serr != SYSLOG_RELAY_E_SUCCESS) { | 642 | if (serr != SYSLOG_RELAY_E_SUCCESS) { |
| 622 | fprintf(stderr, "ERROR: Unable to start capturing syslog.\n"); | 643 | fprintf(stderr, "ERROR: Unable to start capturing syslog.\n"); |
| 623 | syslog_relay_client_free(syslog); | 644 | syslog_relay_client_free(syslog); |
| @@ -626,6 +647,8 @@ static int start_logging(void) | |||
| 626 | device = NULL; | 647 | device = NULL; |
| 627 | return -1; | 648 | return -1; |
| 628 | } | 649 | } |
| 650 | } else { | ||
| 651 | return -1; | ||
| 629 | } | 652 | } |
| 630 | 653 | ||
| 631 | fprintf(stdout, "[connected:%s]\n", udid); | 654 | fprintf(stdout, "[connected:%s]\n", udid); |
| @@ -654,6 +677,19 @@ static void stop_logging(void) | |||
| 654 | } | 677 | } |
| 655 | } | 678 | } |
| 656 | 679 | ||
| 680 | static int write_callback(const void* buf, size_t len, void *user_data) | ||
| 681 | { | ||
| 682 | FILE* f = (FILE*)user_data; | ||
| 683 | ssize_t res = fwrite(buf, 1, len, f); | ||
| 684 | if (res < 0) { | ||
| 685 | return -1; | ||
| 686 | } | ||
| 687 | if (quit_flag > 0) { | ||
| 688 | return -1; | ||
| 689 | } | ||
| 690 | return 0; | ||
| 691 | } | ||
| 692 | |||
| 657 | static void device_event_cb(const idevice_event_t* event, void* userdata) | 693 | static void device_event_cb(const idevice_event_t* event, void* userdata) |
| 658 | { | 694 | { |
| 659 | if (use_network && event->conn_type != CONNECTION_NETWORK) { | 695 | if (use_network && event->conn_type != CONNECTION_NETWORK) { |
| @@ -714,6 +750,15 @@ static void print_usage(int argc, char **argv, int is_error) | |||
| 714 | " --colors force writing colored output, e.g. for --output\n" | 750 | " --colors force writing colored output, e.g. for --output\n" |
| 715 | " --syslog_relay force use of syslog_relay service\n" | 751 | " --syslog_relay force use of syslog_relay service\n" |
| 716 | "\n" | 752 | "\n" |
| 753 | "COMMANDS:\n" | ||
| 754 | " pidlist Print pid and name of all running processes.\n" | ||
| 755 | " archive PATH Request a logarchive and write it to PATH.\n" | ||
| 756 | " Output can be piped to another process using - as PATH.\n" | ||
| 757 | " The file data will be in .tar format.\n" | ||
| 758 | " --start-time VALUE start time of the log data as UNIX timestamp\n" | ||
| 759 | " --age-limit VALUE maximum age of the log data\n" | ||
| 760 | " --size-limit VALUE limit the size of the archive\n" | ||
| 761 | "\n" | ||
| 717 | "FILTER OPTIONS:\n" | 762 | "FILTER OPTIONS:\n" |
| 718 | " -m, --match STRING only print messages that contain STRING\n" | 763 | " -m, --match STRING only print messages that contain STRING\n" |
| 719 | " -M, --unmatch STRING print messages that not contain STRING\n" | 764 | " -M, --unmatch STRING print messages that not contain STRING\n" |
| @@ -761,6 +806,9 @@ int main(int argc, char *argv[]) | |||
| 761 | { "no-colors", no_argument, NULL, 2 }, | 806 | { "no-colors", no_argument, NULL, 2 }, |
| 762 | { "colors", no_argument, NULL, 3 }, | 807 | { "colors", no_argument, NULL, 3 }, |
| 763 | { "syslog_relay", no_argument, NULL, 4 }, | 808 | { "syslog_relay", no_argument, NULL, 4 }, |
| 809 | { "start-time", required_argument, NULL, 5 }, | ||
| 810 | { "size-limit", required_argument, NULL, 6 }, | ||
| 811 | { "age-limit", required_argument, NULL, 7 }, | ||
| 764 | { "output", required_argument, NULL, 'o' }, | 812 | { "output", required_argument, NULL, 'o' }, |
| 765 | { "version", no_argument, NULL, 'v' }, | 813 | { "version", no_argument, NULL, 'v' }, |
| 766 | { NULL, 0, NULL, 0} | 814 | { NULL, 0, NULL, 0} |
| @@ -897,6 +945,15 @@ int main(int argc, char *argv[]) | |||
| 897 | case 4: | 945 | case 4: |
| 898 | force_syslog_relay = 1; | 946 | force_syslog_relay = 1; |
| 899 | break; | 947 | break; |
| 948 | case 5: | ||
| 949 | start_time = strtoll(optarg, NULL, 10); | ||
| 950 | break; | ||
| 951 | case 6: | ||
| 952 | size_limit = strtoll(optarg, NULL, 10); | ||
| 953 | break; | ||
| 954 | case 7: | ||
| 955 | age_limit = strtoll(optarg, NULL, 10); | ||
| 956 | break; | ||
| 900 | case 'o': | 957 | case 'o': |
| 901 | if (!*optarg) { | 958 | if (!*optarg) { |
| 902 | fprintf(stderr, "ERROR: --output option requires an argument!\n"); | 959 | fprintf(stderr, "ERROR: --output option requires an argument!\n"); |
| @@ -969,6 +1026,95 @@ int main(int argc, char *argv[]) | |||
| 969 | argc -= optind; | 1026 | argc -= optind; |
| 970 | argv += optind; | 1027 | argv += optind; |
| 971 | 1028 | ||
| 1029 | if (argc > 0) { | ||
| 1030 | if (!strcmp(argv[0], "pidlist")) { | ||
| 1031 | if (connect_service(1) < 0) { | ||
| 1032 | return 1; | ||
| 1033 | } | ||
| 1034 | plist_t list = NULL; | ||
| 1035 | ostrace_get_pid_list(ostrace, &list); | ||
| 1036 | ostrace_client_free(ostrace); | ||
| 1037 | ostrace = NULL; | ||
| 1038 | idevice_free(device); | ||
| 1039 | device = NULL; | ||
| 1040 | if (!list) { | ||
| 1041 | return 1; | ||
| 1042 | } | ||
| 1043 | plist_sort(list); | ||
| 1044 | plist_dict_iter iter = NULL; | ||
| 1045 | plist_dict_new_iter(list, &iter); | ||
| 1046 | if (iter) { | ||
| 1047 | plist_t node = NULL; | ||
| 1048 | do { | ||
| 1049 | char* key = NULL; | ||
| 1050 | node = NULL; | ||
| 1051 | plist_dict_next_item(list, iter, &key, &node); | ||
| 1052 | if (key) { | ||
| 1053 | printf("%s", key); | ||
| 1054 | free(key); | ||
| 1055 | if (PLIST_IS_DICT(node)) { | ||
| 1056 | plist_t pname = plist_dict_get_item(node, "ProcessName"); | ||
| 1057 | if (PLIST_IS_STRING(pname)) { | ||
| 1058 | printf(" %s", plist_get_string_ptr(pname, NULL)); | ||
| 1059 | } | ||
| 1060 | } | ||
| 1061 | printf("\n"); | ||
| 1062 | } | ||
| 1063 | } while (node); | ||
| 1064 | plist_mem_free(iter); | ||
| 1065 | } | ||
| 1066 | plist_free(list); | ||
| 1067 | return 0; | ||
| 1068 | } else if (!strcmp(argv[0], "archive")) { | ||
| 1069 | if (force_syslog_relay) { | ||
| 1070 | force_syslog_relay = 0; | ||
| 1071 | } | ||
| 1072 | if (argc < 2) { | ||
| 1073 | fprintf(stderr, "Please specify an output filename.\n"); | ||
| 1074 | return 1; | ||
| 1075 | } | ||
| 1076 | FILE* outf = NULL; | ||
| 1077 | if (!strcmp(argv[1], "-")) { | ||
| 1078 | if (isatty(1)) { | ||
| 1079 | fprintf(stderr, "Refusing to directly write to stdout. Pipe the output to another process.\n"); | ||
| 1080 | return 1; | ||
| 1081 | } | ||
| 1082 | outf = stdout; | ||
| 1083 | } else { | ||
| 1084 | outf = fopen(argv[1], "w"); | ||
| 1085 | } | ||
| 1086 | if (!outf) { | ||
| 1087 | fprintf(stderr, "Failed to open %s: %s\n", argv[1], strerror(errno)); | ||
| 1088 | return 1; | ||
| 1089 | } | ||
| 1090 | if (connect_service(1) < 0) { | ||
| 1091 | if (outf != stdout) { | ||
| 1092 | fclose(outf); | ||
| 1093 | } | ||
| 1094 | return 1; | ||
| 1095 | } | ||
| 1096 | plist_t options = plist_new_dict(); | ||
| 1097 | if (start_time > 0) { | ||
| 1098 | plist_dict_set_item(options, "StartTime", plist_new_int(start_time)); | ||
| 1099 | } | ||
| 1100 | if (size_limit > 0) { | ||
| 1101 | plist_dict_set_item(options, "SizeLimit", plist_new_int(size_limit)); | ||
| 1102 | } | ||
| 1103 | if (age_limit > 0) { | ||
| 1104 | plist_dict_set_item(options, "AgeLimit", plist_new_int(age_limit)); | ||
| 1105 | } | ||
| 1106 | ostrace_create_archive(ostrace, options, write_callback, outf); | ||
| 1107 | ostrace_client_free(ostrace); | ||
| 1108 | ostrace = NULL; | ||
| 1109 | idevice_free(device); | ||
| 1110 | device = NULL; | ||
| 1111 | if (outf != stdout) { | ||
| 1112 | fclose(outf); | ||
| 1113 | } | ||
| 1114 | return 0; | ||
| 1115 | } | ||
| 1116 | } | ||
| 1117 | |||
| 972 | int num = 0; | 1118 | int num = 0; |
| 973 | idevice_info_t *devices = NULL; | 1119 | idevice_info_t *devices = NULL; |
| 974 | idevice_get_device_list_extended(&devices, &num); | 1120 | idevice_get_device_list_extended(&devices, &num); |
| @@ -976,7 +1122,7 @@ int main(int argc, char *argv[]) | |||
| 976 | if (num == 0) { | 1122 | if (num == 0) { |
| 977 | if (!udid) { | 1123 | if (!udid) { |
| 978 | fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n"); | 1124 | fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n"); |
| 979 | return -1; | 1125 | return 1; |
| 980 | } | 1126 | } |
| 981 | 1127 | ||
| 982 | fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid); | 1128 | fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid); |
