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); |