/* * idevicesyslog.c * Relay the syslog of a device to stdout * * Copyright (c) 2010-2020 Nikias Bassen, All Rights Reserved. * Copyright (c) 2009 Martin Szulecki 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define TOOL_NAME "idevicesyslog" #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #define sleep(x) Sleep(x*1000) #endif #include #include #include #include static int quit_flag = 0; static int exit_on_disconnect = 0; static int show_device_name = 0; static int force_syslog_relay = 0; static char* udid = NULL; static char** proc_filters = NULL; static int num_proc_filters = 0; static int proc_filter_excluding = 0; static int* pid_filters = NULL; static int num_pid_filters = 0; static char** msg_filters = NULL; static int num_msg_filters = 0; static char** msg_reverse_filters = NULL; static int num_msg_reverse_filters = 0; static char** trigger_filters = NULL; static int num_trigger_filters = 0; static char** untrigger_filters = NULL; static int num_untrigger_filters = 0; static int triggered = 0; static idevice_t device = NULL; static syslog_relay_client_t syslog = NULL; static ostrace_client_t ostrace = NULL; static const char QUIET_FILTER[] = "CircleJoinRequested|CommCenter|HeuristicInterpreter|MobileMail|PowerUIAgent|ProtectedCloudKeySyncing|SpringBoard|UserEventAgent|WirelessRadioManagerd|accessoryd|accountsd|aggregated|analyticsd|appstored|apsd|assetsd|assistant_service|backboardd|biometrickitd|bluetoothd|calaccessd|callservicesd|cloudd|com.apple.Safari.SafeBrowsing.Service|contextstored|corecaptured|coreduetd|corespeechd|cdpd|dasd|dataaccessd|distnoted|dprivacyd|duetexpertd|findmydeviced|fmfd|fmflocatord|gpsd|healthd|homed|identityservicesd|imagent|itunescloudd|itunesstored|kernel|locationd|maild|mDNSResponder|mediaremoted|mediaserverd|mobileassetd|nanoregistryd|nanotimekitcompaniond|navd|nsurlsessiond|passd|pasted|photoanalysisd|powerd|powerlogHelperd|ptpd|rapportd|remindd|routined|runningboardd|searchd|sharingd|suggestd|symptomsd|timed|thermalmonitord|useractivityd|vmd|wifid|wirelessproxd"; static int use_network = 0; static long long start_time = -1; static long long size_limit = -1; static long long age_limit = -1; static char *line = NULL; static int line_buffer_size = 0; static int lp = 0; static void add_filter(const char* filterstr) { int filter_len = strlen(filterstr); const char* start = filterstr; const char* end = filterstr + filter_len; const char* p = start; while (p <= end) { if ((*p == '|') || (*p == '\0')) { if (p-start > 0) { char* procn = malloc(p-start+1); if (!procn) { fprintf(stderr, "ERROR: malloc() failed\n"); exit(EXIT_FAILURE); } memcpy(procn, start, p-start); procn[p-start] = '\0'; char* endp = NULL; int pid_value = (int)strtol(procn, &endp, 10); if (!endp || *endp == 0) { int *new_pid_filters = realloc(pid_filters, sizeof(int) * (num_pid_filters+1)); if (!new_pid_filters) { fprintf(stderr, "ERROR: realloc() failed\n"); exit(EXIT_FAILURE); } pid_filters = new_pid_filters; pid_filters[num_pid_filters] = pid_value; num_pid_filters++; } else { char **new_proc_filters = realloc(proc_filters, sizeof(char*) * (num_proc_filters+1)); if (!new_proc_filters) { fprintf(stderr, "ERROR: realloc() failed\n"); exit(EXIT_FAILURE); } proc_filters = new_proc_filters; proc_filters[num_proc_filters] = procn; num_proc_filters++; } } start = p+1; } p++; } } static int find_char(char c, char** p, const char* end) { while ((**p != c) && (*p < end)) { (*p)++; } return (**p == c); } static void stop_logging(void); static int message_filter_matching(const char* message) { if (num_msg_filters > 0) { int found = 0; int i; for (i = 0; i < num_msg_filters; i++) { if (strstr(message, msg_filters[i])) { found = 1; break; } } if (!found) { return 0; } } if (num_msg_reverse_filters > 0) { int found = 0; int i; for (i = 0; i < num_msg_reverse_filters; i++) { if (strstr(message, msg_reverse_filters[i])) { found = 1; break; } } if (found) { return 0; } } return 1; } static int process_filter_matching(int pid, const char* process_name, int process_name_length) { int proc_matched = 0; if (num_pid_filters > 0) { int found = proc_filter_excluding; int i = 0; for (i = 0; i < num_pid_filters; i++) { if (pid == pid_filters[i]) { found = !proc_filter_excluding; break; } } if (found) { proc_matched = 1; } } if (num_proc_filters > 0 && !proc_matched) { int found = proc_filter_excluding; int i = 0; for (i = 0; i < num_proc_filters; i++) { if (!proc_filters[i]) continue; if (strncmp(proc_filters[i], process_name, process_name_length) == 0) { found = !proc_filter_excluding; break; } } if (found) { proc_matched = 1; } } return proc_matched; } static void syslog_callback(char c, void *user_data) { if (lp >= line_buffer_size-1) { line_buffer_size+=1024; char* _line = realloc(line, line_buffer_size); if (!_line) { fprintf(stderr, "ERROR: realloc failed\n"); exit(EXIT_FAILURE); } line = _line; } line[lp++] = c; if (c == '\0') { int shall_print = 0; int trigger_off = 0; lp--; char* linep = &line[0]; do { if (lp < 16) { shall_print = 1; cprintf(FG_WHITE); break; } if (line[3] == ' ' && line[6] == ' ' && line[15] == ' ') { char* end = &line[lp]; char* p = &line[16]; /* device name */ char* device_name_start = p; char* device_name_end = p; if (!find_char(' ', &p, end)) break; device_name_end = p; p++; /* check if we have any triggers/untriggers */ if (num_untrigger_filters > 0 && triggered) { int found = 0; int i; for (i = 0; i < num_untrigger_filters; i++) { if (strstr(device_name_end+1, untrigger_filters[i])) { found = 1; break; } } if (!found) { shall_print = 1; } else { shall_print = 1; trigger_off = 1; } } else if (num_trigger_filters > 0 && !triggered) { int found = 0; int i; for (i = 0; i < num_trigger_filters; i++) { if (strstr(device_name_end+1, trigger_filters[i])) { found = 1; break; } } if (!found) { shall_print = 0; break; } triggered = 1; shall_print = 1; } else if (num_trigger_filters == 0 && num_untrigger_filters > 0 && !triggered) { shall_print = 0; quit_flag++; break; } /* check message filters */ shall_print = message_filter_matching(device_name_end+1); if (!shall_print) { break; } /* process name */ char* proc_name_start = p; char* proc_name_end = p; if (!find_char('[', &p, end)) break; char* process_name_start = proc_name_start; char* process_name_end = p; char* pid_start = p+1; char* pp = process_name_start; if (find_char('(', &pp, p)) { process_name_end = pp; } if (!find_char(']', &p, end)) break; p++; if (*p != ' ') break; proc_name_end = p; p++; /* match pid / process name */ char* endp = NULL; int pid_value = (int)strtol(pid_start, &endp, 10); if (process_filter_matching(pid_value, process_name_start, process_name_end-process_name_start)) { shall_print = 1; } else { if (num_pid_filters > 0 || num_proc_filters > 0) { shall_print = 0; break; } } /* log level */ char* level_start = p; char* level_end = p; const char* level_color = NULL; if (!strncmp(p, ":", 9)) { level_end += 9; level_color = FG_GREEN; } else if (!strncmp(p, ":", 8)) { level_end += 8; level_color = FG_RED; } else if (!strncmp(p, ":", 10)) { level_end += 10; level_color = FG_YELLOW; } else if (!strncmp(p, ":", 8)) { level_end += 8; level_color = FG_MAGENTA; } else { level_color = FG_WHITE; } /* write date and time */ cprintf(FG_LIGHT_GRAY); fwrite(line, 1, 16, stdout); if (show_device_name) { /* write device name */ cprintf(FG_DARK_YELLOW); fwrite(device_name_start, 1, device_name_end-device_name_start+1, stdout); cprintf(COLOR_RESET); } /* write process name */ cprintf(FG_BRIGHT_CYAN); fwrite(process_name_start, 1, process_name_end-process_name_start, stdout); cprintf(FG_CYAN); fwrite(process_name_end, 1, proc_name_end-process_name_end+1, stdout); /* write log level */ cprintf(level_color); if (level_end > level_start) { fwrite(level_start, 1, level_end-level_start, stdout); p = level_end; } lp -= p - linep; linep = p; cprintf(FG_WHITE); } else { shall_print = 1; cprintf(FG_WHITE); } } while (0); if ((num_msg_filters == 0 && num_msg_reverse_filters == 0 && num_proc_filters == 0 && num_pid_filters == 0 && num_trigger_filters == 0 && num_untrigger_filters == 0) || shall_print) { fwrite(linep, 1, lp, stdout); cprintf(COLOR_RESET); fflush(stdout); if (trigger_off) { triggered = 0; } } line[0] = '\0'; lp = 0; return; } } static void ostrace_syslog_callback(const void* buf, size_t len, void* user_data) { if (len < 0x81) { fprintf(stderr, "Error: not enough data in callback function?!\n"); return; } struct ostrace_packet_header_t *trace_hdr = (struct ostrace_packet_header_t*)buf; if (trace_hdr->marker != 2 || (trace_hdr->type != 8 && trace_hdr->type != 2)) { fprintf(stderr, "unexpected packet data %02x %08x\n", trace_hdr->marker, trace_hdr->type); } const char* dataptr = (const char*)buf + trace_hdr->header_size; const char* process_name = dataptr; const char* image_name = (trace_hdr->imagepath_len > 0) ? dataptr + trace_hdr->procpath_len : NULL; const char* message = (trace_hdr->message_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len : NULL; //const char* subsystem = (trace_hdr->subsystem_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len + trace_hdr->message_len : NULL; //const char* category = (trace_hdr->category_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len + trace_hdr->message_len + trace_hdr->subsystem_len : NULL; int shall_print = 1; int trigger_off = 0; const char* process_name_short = (process_name) ? strrchr(process_name, '/') : ""; process_name_short = (process_name_short) ? process_name_short+1 : process_name; const char* image_name_short = (image_name) ? strrchr(image_name, '/') : NULL; image_name_short = (image_name_short) ? image_name_short+1 : process_name; if (image_name_short && !strcmp(image_name_short, process_name_short)) { image_name_short = NULL; } do { /* check if we have any triggers/untriggers */ if (num_untrigger_filters > 0 && triggered) { int found = 0; int i; for (i = 0; i < num_untrigger_filters; i++) { if (strstr(message, untrigger_filters[i])) { found = 1; break; } } if (!found) { shall_print = 1; } else { shall_print = 1; trigger_off = 1; } } else if (num_trigger_filters > 0 && !triggered) { int found = 0; int i; for (i = 0; i < num_trigger_filters; i++) { if (strstr(message, trigger_filters[i])) { found = 1; break; } } if (!found) { shall_print = 0; break; } triggered = 1; shall_print = 1; } else if (num_trigger_filters == 0 && num_untrigger_filters > 0 && !triggered) { shall_print = 0; quit_flag++; break; } /* check message filters */ shall_print = message_filter_matching(message); if (!shall_print) { break; } /* check process filters */ if (process_filter_matching(trace_hdr->pid, process_name_short, strlen(process_name_short))) { shall_print = 1; } else { if (num_pid_filters > 0 || num_proc_filters > 0) { shall_print = 0; } } if (!shall_print) { break; } } while (0); if (!shall_print) { return; } const char* level_str = "Unknown"; const char* level_color = FG_YELLOW; switch (trace_hdr->level) { case 0: level_str = "Notice"; level_color = FG_GREEN; break; case 0x01: level_str = "Info"; level_color = FG_WHITE; break; case 0x02: level_str = "Debug"; level_color = FG_MAGENTA; break; case 0x10: level_str = "Error"; level_color = FG_RED; break; case 0x11: level_str = "Fault"; level_color = FG_RED; default: break; } char datebuf[24]; struct tm *tp; time_t time_sec = (time_t)trace_hdr->time_sec; #ifdef HAVE_LOCALTIME_R struct tm tp_ = {0, }; tp = localtime_r(&time_sec, &tp_); #else tp = localtime(&time_sec); #endif #ifdef _WIN32 strftime(datebuf, 16, "%b %#d %H:%M:%S", tp); #else strftime(datebuf, 16, "%b %e %H:%M:%S", tp); #endif snprintf(datebuf+15, 9, ".%06u", trace_hdr->time_usec); /* write date and time */ cprintf(FG_LIGHT_GRAY "%s ", datebuf); if (show_device_name) { /* write device name TODO do we need this? */ //cprintf(FG_DARK_YELLOW "%s ", device_name); } /* write process name */ cprintf(FG_BRIGHT_CYAN "%s" FG_CYAN, process_name_short); if (image_name_short) { cprintf("(%s)", image_name_short); } cprintf("[%d]" COLOR_RESET " ", trace_hdr->pid); /* write log level */ cprintf(level_color); cprintf("<%s>:" COLOR_RESET " ", level_str); /* write message */ cprintf(FG_WHITE); cprintf("%s" COLOR_RESET "\n", message); fflush(stdout); if (trigger_off) { triggered = 0; } } static plist_t get_pid_list() { plist_t list = NULL; ostrace_client_t ostrace_tmp = NULL; ostrace_client_start_service(device, &ostrace_tmp, TOOL_NAME); if (ostrace_tmp) { ostrace_get_pid_list(ostrace_tmp, &list); ostrace_client_free(ostrace_tmp); } return list; } static int pid_valid(int pid) { plist_t list = get_pid_list(); if (!list) return 0; char valbuf[16]; snprintf(valbuf, 16, "%d", pid); return (plist_dict_get_item(list, valbuf)) ? 1 : 0; } static int pid_for_proc(const char* procname) { int result = -1; plist_t list = get_pid_list(); if (!list) { return result; } plist_dict_iter iter = NULL; plist_dict_new_iter(list, &iter); if (iter) { plist_t node = NULL; do { char* key = NULL; node = NULL; plist_dict_next_item(list, iter, &key, &node); if (!key) { break; } if (PLIST_IS_DICT(node)) { plist_t pname = plist_dict_get_item(node, "ProcessName"); if (PLIST_IS_STRING(pname)) { if (!strcmp(plist_get_string_ptr(pname, NULL), procname)) { result = (int)strtol(key, NULL, 10); } } } free(key); } while (node); plist_mem_free(iter); } plist_free(list); return result; } static int connect_service(int ostrace_required) { if (!device) { idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); if (ret != IDEVICE_E_SUCCESS) { fprintf(stderr, "Device with udid %s not found!?\n", udid); return -1; } } lockdownd_client_t lockdown = NULL; lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME); if (lerr != LOCKDOWN_E_SUCCESS) { fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr); idevice_free(device); device = NULL; return -1; } lockdownd_service_descriptor_t svc = NULL; const char* service_name = OSTRACE_SERVICE_NAME; int use_ostrace = 1; if (idevice_get_device_version(device) < IDEVICE_DEVICE_VERSION(9,0,0) || force_syslog_relay) { service_name = SYSLOG_RELAY_SERVICE_NAME; use_ostrace = 0; } if (ostrace_required && !use_ostrace) { fprintf(stderr, "ERROR: This operation requires iOS 9 or later.\n"); lockdownd_client_free(lockdown); idevice_free(device); device = NULL; return -1; } /* start syslog_relay/os_trace_relay service */ lerr = lockdownd_start_service(lockdown, service_name, &svc); if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) { fprintf(stderr, "*** Device is passcode protected, enter passcode on the device to continue ***\n"); while (!quit_flag) { lerr = lockdownd_start_service(lockdown, service_name, &svc); if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) { break; } sleep(1); } } if (lerr != LOCKDOWN_E_SUCCESS) { fprintf(stderr, "ERROR: Could not start %s service: %s (%d)\n", service_name, lockdownd_strerror(lerr), lerr); idevice_free(device); device = NULL; return -1; } lockdownd_client_free(lockdown); if (use_ostrace) { /* connect to os_trace_relay service */ ostrace_error_t serr = OSTRACE_E_UNKNOWN_ERROR; serr = ostrace_client_new(device, svc, &ostrace); lockdownd_service_descriptor_free(svc); if (serr != OSTRACE_E_SUCCESS) { fprintf(stderr, "ERROR: Could not connect to %s service (%d)\n", service_name, serr); idevice_free(device); device = NULL; return -1; } } else { /* connect to syslog_relay service */ syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR; serr = syslog_relay_client_new(device, svc, &syslog); lockdownd_service_descriptor_free(svc); if (serr != SYSLOG_RELAY_E_SUCCESS) { fprintf(stderr, "ERROR: Could not connect to %s service (%d)\n", service_name, serr); idevice_free(device); device = NULL; return -1; } } return 0; } static int start_logging(void) { if (connect_service(0) < 0) { return -1; } /* start capturing syslog */ if (ostrace) { plist_t options = plist_new_dict(); if (num_proc_filters == 0 && num_pid_filters == 1 && !proc_filter_excluding) { if (pid_filters[0] > 0) { if (!pid_valid(pid_filters[0])) { fprintf(stderr, "NOTE: A process with pid doesn't exists!\n"); } } plist_dict_set_item(options, "Pid", plist_new_int(pid_filters[0])); } else if (num_proc_filters == 1 && num_pid_filters == 0 && !proc_filter_excluding) { int pid = pid_for_proc(proc_filters[0]); if (!strcmp(proc_filters[0], "kernel")) { pid = 0; } if (pid >= 0) { plist_dict_set_item(options, "Pid", plist_new_int(pid)); } } ostrace_error_t serr = ostrace_start_activity(ostrace, options, ostrace_syslog_callback, NULL); if (serr != OSTRACE_E_SUCCESS) { fprintf(stderr, "ERROR: Unable to start capturing syslog.\n"); ostrace_client_free(ostrace); ostrace = NULL; idevice_free(device); device = NULL; return -1; } } else if (syslog) { syslog_relay_error_t serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL); if (serr != SYSLOG_RELAY_E_SUCCESS) { fprintf(stderr, "ERROR: Unable to start capturing syslog.\n"); syslog_relay_client_free(syslog); syslog = NULL; idevice_free(device); device = NULL; return -1; } } else { return -1; } fprintf(stdout, "[connected:%s]\n", udid); fflush(stdout); return 0; } static void stop_logging(void) { fflush(stdout); if (syslog) { syslog_relay_client_free(syslog); syslog = NULL; } if (ostrace) { ostrace_stop_activity(ostrace); ostrace_client_free(ostrace); ostrace = NULL; } if (device) { idevice_free(device); device = NULL; } } static int write_callback(const void* buf, size_t len, void *user_data) { FILE* f = (FILE*)user_data; ssize_t res = fwrite(buf, 1, len, f); if (res < 0) { return -1; } if (quit_flag > 0) { return -1; } return 0; } static void print_sorted_pidlist(plist_t list) { struct listelem; struct listelem { int val; struct listelem *next; }; struct listelem* sortedlist = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(list, &iter); if (iter) { plist_t node = NULL; do { char* key = NULL; node = NULL; plist_dict_next_item(list, iter, &key, &node); if (key) { int pidval = (int)strtol(key, NULL, 10); struct listelem* elem = (struct listelem*)malloc(sizeof(struct listelem)); elem->val = pidval; elem->next = NULL; struct listelem* prev = NULL; struct listelem* curr = sortedlist; while (curr && pidval > curr->val) { prev = curr; curr = curr->next; } elem->next = curr; if (prev == NULL) { sortedlist = elem; } else { prev->next = elem; } free(key); } } while (node); plist_mem_free(iter); } struct listelem *listp = sortedlist; char pidstr[16]; while (listp) { snprintf(pidstr, 16, "%d", listp->val); plist_t node = plist_dict_get_item(list, pidstr); if (PLIST_IS_DICT(node)) { plist_t pname = plist_dict_get_item(node, "ProcessName"); if (PLIST_IS_STRING(pname)) { printf("%d %s\n", listp->val, plist_get_string_ptr(pname, NULL)); } } struct listelem *curr = listp; listp = listp->next; free(curr); } } static void device_event_cb(const idevice_event_t* event, void* userdata) { if (use_network && event->conn_type != CONNECTION_NETWORK) { return; } if (!use_network && event->conn_type != CONNECTION_USBMUXD) { return; } if (event->event == IDEVICE_DEVICE_ADD) { if (!syslog && !ostrace) { if (!udid) { udid = strdup(event->udid); } if (strcmp(udid, event->udid) == 0) { if (start_logging() != 0) { fprintf(stderr, "Could not start logger for udid %s\n", udid); } } } } else if (event->event == IDEVICE_DEVICE_REMOVE) { if ((syslog || ostrace) && (strcmp(udid, event->udid) == 0)) { stop_logging(); fprintf(stdout, "[disconnected:%s]\n", udid); if (exit_on_disconnect) { quit_flag++; } } } } /** * signal handler function for cleaning up properly */ static void clean_exit(int sig) { fprintf(stderr, "\nExiting...\n"); quit_flag++; } static void print_usage(int argc, char **argv, int is_error) { char *name = strrchr(argv[0], '/'); fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0])); fprintf(is_error ? stderr : stdout, "\n" "Relay syslog of a connected device.\n" "\n" "OPTIONS:\n" " -u, --udid UDID target specific device by UDID\n" " -n, --network connect to network device\n" " -x, --exit exit when device disconnects\n" " -h, --help prints usage information\n" " -d, --debug enable communication debugging\n" " -v, --version prints version information\n" " --no-colors disable colored output\n" " -o, --output FILE write to FILE instead of stdout\n" " (existing FILE will be overwritten)\n" " --colors force writing colored output, e.g. for --output\n" " --syslog-relay force use of syslog_relay service\n" "\n" "COMMANDS:\n" " pidlist Print pid and name of all running processes.\n" " archive PATH Request a logarchive and write it to PATH.\n" " Output can be piped to another process using - as PATH.\n" " The file data will be in .tar format.\n" " --start-time VALUE start time of the log data as UNIX timestamp\n" " --age-limit VALUE maximum age of the log data\n" " --size-limit VALUE limit the size of the archive\n" "\n" "FILTER OPTIONS:\n" " -m, --match STRING only print messages that contain STRING\n" " -M, --unmatch STRING print messages that not contain STRING\n" " -t, --trigger STRING start logging when matching STRING\n" " -T, --untrigger STRING stop logging when matching STRING\n" " -p, --process PROCESS only print messages from matching process(es)\n" " -e, --exclude PROCESS print all messages except matching process(es)\n" " PROCESS is a process name or multiple process names\n" " separated by \"|\".\n" " -q, --quiet set a filter to exclude common noisy processes\n" " --quiet-list prints the list of processes for --quiet and exits\n" " -k, --kernel only print kernel messages\n" " -K, --no-kernel suppress kernel messages\n" "\n" "For filter examples consult idevicesyslog(1) man page.\n" "\n" "Homepage: <" PACKAGE_URL ">\n" "Bug Reports: <" PACKAGE_BUGREPORT ">\n" ); } int main(int argc, char *argv[]) { int include_filter = 0; int exclude_filter = 0; int include_kernel = 0; int exclude_kernel = 0; int force_colors = 0; int c = 0; const struct option longopts[] = { { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "udid", required_argument, NULL, 'u' }, { "network", no_argument, NULL, 'n' }, { "exit", no_argument, NULL, 'x' }, { "trigger", required_argument, NULL, 't' }, { "untrigger", required_argument, NULL, 'T' }, { "match", required_argument, NULL, 'm' }, { "process", required_argument, NULL, 'p' }, { "exclude", required_argument, NULL, 'e' }, { "quiet", no_argument, NULL, 'q' }, { "kernel", no_argument, NULL, 'k' }, { "no-kernel", no_argument, NULL, 'K' }, { "quiet-list", no_argument, NULL, 1 }, { "no-colors", no_argument, NULL, 2 }, { "colors", no_argument, NULL, 3 }, { "syslog_relay", no_argument, NULL, 4 }, { "syslog-relay", no_argument, NULL, 4 }, { "legacy", no_argument, NULL, 4 }, { "start-time", required_argument, NULL, 5 }, { "size-limit", required_argument, NULL, 6 }, { "age-limit", required_argument, NULL, 7 }, { "output", required_argument, NULL, 'o' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0} }; signal(SIGINT, clean_exit); signal(SIGTERM, clean_exit); #ifndef _WIN32 signal(SIGQUIT, clean_exit); signal(SIGPIPE, SIG_IGN); #endif while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:M:e:p:qkKo:v", longopts, NULL)) != -1) { switch (c) { case 'd': idevice_set_debug_level(1); break; case 'u': if (!*optarg) { fprintf(stderr, "ERROR: UDID must not be empty!\n"); print_usage(argc, argv, 1); return 2; } free(udid); udid = strdup(optarg); break; case 'n': use_network = 1; break; case 'q': exclude_filter++; add_filter(QUIET_FILTER); break; case 'p': case 'e': if (c == 'p') { include_filter++; } else if (c == 'e') { exclude_filter++; } if (!*optarg) { fprintf(stderr, "ERROR: filter string must not be empty!\n"); print_usage(argc, argv, 1); return 2; } add_filter(optarg); break; case 'm': if (!*optarg) { fprintf(stderr, "ERROR: message filter string must not be empty!\n"); print_usage(argc, argv, 1); return 2; } else { char **new_msg_filters = realloc(msg_filters, sizeof(char*) * (num_msg_filters+1)); if (!new_msg_filters) { fprintf(stderr, "ERROR: realloc() failed\n"); exit(EXIT_FAILURE); } msg_filters = new_msg_filters; msg_filters[num_msg_filters] = strdup(optarg); num_msg_filters++; } break; case 'M': if (!*optarg) { fprintf(stderr, "ERROR: reverse message filter string must not be empty!\n"); print_usage(argc, argv, 1); return 2; } else { char **new_msg_filters = realloc(msg_reverse_filters, sizeof(char*) * (num_msg_reverse_filters+1)); if (!new_msg_filters) { fprintf(stderr, "ERROR: realloc() failed\n"); exit(EXIT_FAILURE); } msg_reverse_filters = new_msg_filters; msg_reverse_filters[num_msg_reverse_filters] = strdup(optarg); num_msg_reverse_filters++; } break; case 't': if (!*optarg) { fprintf(stderr, "ERROR: trigger filter string must not be empty!\n"); print_usage(argc, argv, 1); return 2; } else { char **new_trigger_filters = realloc(trigger_filters, sizeof(char*) * (num_trigger_filters+1)); if (!new_trigger_filters) { fprintf(stderr, "ERROR: realloc() failed\n"); exit(EXIT_FAILURE); } trigger_filters = new_trigger_filters; trigger_filters[num_trigger_filters] = strdup(optarg); num_trigger_filters++; } break; case 'T': if (!*optarg) { fprintf(stderr, "ERROR: untrigger filter string must not be empty!\n"); print_usage(argc, argv, 1); return 2; } else { char **new_untrigger_filters = realloc(untrigger_filters, sizeof(char*) * (num_untrigger_filters+1)); if (!new_untrigger_filters) { fprintf(stderr, "ERROR: realloc() failed\n"); exit(EXIT_FAILURE); } untrigger_filters = new_untrigger_filters; untrigger_filters[num_untrigger_filters] = strdup(optarg); num_untrigger_filters++; } break; case 'k': include_kernel++; break; case 'K': exclude_kernel++; break; case 'x': exit_on_disconnect = 1; break; case 'h': print_usage(argc, argv, 0); return 0; case 1: { printf("%s\n", QUIET_FILTER); return 0; } case 2: term_colors_set_enabled(0); break; case 3: force_colors = 1; break; case 4: force_syslog_relay = 1; break; case 5: start_time = strtoll(optarg, NULL, 10); break; case 6: size_limit = strtoll(optarg, NULL, 10); break; case 7: age_limit = strtoll(optarg, NULL, 10); break; case 'o': if (!*optarg) { fprintf(stderr, "ERROR: --output option requires an argument!\n"); print_usage(argc, argv, 1); return 2; } else { if (freopen(optarg, "w", stdout) == NULL) { fprintf(stderr, "ERROR: Failed to open output file '%s' for writing: %s\n", optarg, strerror(errno)); return 1; } term_colors_set_enabled(0); } break; case 'v': printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); return 0; default: print_usage(argc, argv, 1); return 2; } } if (force_colors) { term_colors_set_enabled(1); } if (include_kernel > 0 && exclude_kernel > 0) { fprintf(stderr, "ERROR: -k and -K cannot be used together.\n"); print_usage(argc, argv, 1); return 2; } if (include_filter > 0 && exclude_filter > 0) { fprintf(stderr, "ERROR: -p and -e/-q cannot be used together.\n"); print_usage(argc, argv, 1); return 2; } if (include_filter > 0 && exclude_kernel > 0) { fprintf(stderr, "ERROR: -p and -K cannot be used together.\n"); print_usage(argc, argv, 1); return 2; } if (exclude_filter > 0) { proc_filter_excluding = 1; if (include_kernel) { int i = 0; for (i = 0; i < num_proc_filters; i++) { if (!strcmp(proc_filters[i], "kernel")) { free(proc_filters[i]); proc_filters[i] = NULL; } } } else if (exclude_kernel) { add_filter("kernel"); } } else { if (include_kernel) { add_filter("kernel"); } else if (exclude_kernel) { proc_filter_excluding = 1; add_filter("kernel"); } } if (num_untrigger_filters > 0 && num_trigger_filters == 0) { triggered = 1; } argc -= optind; argv += optind; if (argc > 0) { if (!strcmp(argv[0], "pidlist")) { if (connect_service(1) < 0) { return 1; } plist_t list = NULL; ostrace_get_pid_list(ostrace, &list); ostrace_client_free(ostrace); ostrace = NULL; idevice_free(device); device = NULL; if (!list) { return 1; } print_sorted_pidlist(list); plist_free(list); return 0; } else if (!strcmp(argv[0], "archive")) { if (force_syslog_relay) { force_syslog_relay = 0; } if (argc < 2) { fprintf(stderr, "Please specify an output filename.\n"); return 1; } FILE* outf = NULL; if (!strcmp(argv[1], "-")) { if (isatty(1)) { fprintf(stderr, "Refusing to directly write to stdout. Pipe the output to another process.\n"); return 1; } outf = stdout; } else { outf = fopen(argv[1], "w"); } if (!outf) { fprintf(stderr, "Failed to open %s: %s\n", argv[1], strerror(errno)); return 1; } if (connect_service(1) < 0) { if (outf != stdout) { fclose(outf); } return 1; } plist_t options = plist_new_dict(); if (start_time > 0) { plist_dict_set_item(options, "StartTime", plist_new_int(start_time)); } if (size_limit > 0) { plist_dict_set_item(options, "SizeLimit", plist_new_int(size_limit)); } if (age_limit > 0) { plist_dict_set_item(options, "AgeLimit", plist_new_int(age_limit)); } ostrace_create_archive(ostrace, options, write_callback, outf); ostrace_client_free(ostrace); ostrace = NULL; idevice_free(device); device = NULL; if (outf != stdout) { fclose(outf); } return 0; } else { fprintf(stderr, "Unknown command '%s'. See --help for valid commands.\n", argv[0]); return 1; } } int num = 0; idevice_info_t *devices = NULL; idevice_get_device_list_extended(&devices, &num); int count = 0; for (int i = 0; i < num; i++) { if (devices[i]->conn_type == CONNECTION_NETWORK && use_network) { count++; } else if (devices[i]->conn_type == CONNECTION_USBMUXD) { count++; } } idevice_device_list_extended_free(devices); if (count == 0) { if (!udid) { fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n"); return 1; } fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid); } line_buffer_size = 1024; line = malloc(line_buffer_size); idevice_subscription_context_t context = NULL; idevice_events_subscribe(&context, device_event_cb, NULL); while (!quit_flag) { sleep(1); } idevice_events_unsubscribe(context); stop_logging(); if (num_proc_filters > 0) { int i; for (i = 0; i < num_proc_filters; i++) { free(proc_filters[i]); } free(proc_filters); } if (num_pid_filters > 0) { free(pid_filters); } if (num_msg_filters > 0) { int i; for (i = 0; i < num_msg_filters; i++) { free(msg_filters[i]); } free(msg_filters); } if (num_msg_reverse_filters > 0) { int i; for (i = 0; i < num_msg_reverse_filters; i++) { free(msg_reverse_filters[i]); } free(msg_reverse_filters); } if (num_trigger_filters > 0) { int i; for (i = 0; i < num_trigger_filters; i++) { free(trigger_filters[i]); } free(trigger_filters); } if (num_untrigger_filters > 0) { int i; for (i = 0; i < num_untrigger_filters; i++) { free(untrigger_filters[i]); } free(untrigger_filters); } free(line); free(udid); return 0; }