diff --git a/MobileDevice.framework b/MobileDevice.framework deleted file mode 120000 index 40d3ede..0000000 --- a/MobileDevice.framework +++ /dev/null @@ -1 +0,0 @@ -/System/Library/PrivateFrameworks/MobileDevice.framework \ No newline at end of file diff --git a/deviceconsole.xcodeproj/project.pbxproj b/deviceconsole.xcodeproj/project.pbxproj index 8ac9a7b..67577d7 100644 --- a/deviceconsole.xcodeproj/project.pbxproj +++ b/deviceconsole.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ /* Begin PBXFileReference section */ 08FB7796FE84155DC02AAC07 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; + 8B6CE20D1E19B11F003E38F4 /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = ""; }; 8DD76FB20486AB0100D96B5E /* deviceconsole */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = deviceconsole; sourceTree = BUILT_PRODUCTS_DIR; }; 945855DB140EB622009DFEA5 /* MobileDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MobileDevice.h; sourceTree = ""; }; 945856A9140EC3BA009DFEA5 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; @@ -61,6 +62,7 @@ children = ( 945855DB140EB622009DFEA5 /* MobileDevice.h */, 08FB7796FE84155DC02AAC07 /* main.c */, + 8B6CE20D1E19B11F003E38F4 /* main.h */, ); name = Source; sourceTree = ""; @@ -99,6 +101,8 @@ /* Begin PBXProject section */ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "deviceconsole" */; compatibilityVersion = "Xcode 3.1"; developmentRegion = English; diff --git a/main.c b/main.c index f294ca3..a5d6a06 100644 --- a/main.c +++ b/main.c @@ -1,20 +1,10 @@ #include +#include #include -#include -#include "MobileDevice.h" +#include "main.h" -typedef struct { - service_conn_t connection; - CFSocketRef socket; - CFRunLoopSourceRef source; -} DeviceConsoleConnection; - -static CFMutableDictionaryRef liveConnections; -static int debug; -static CFStringRef requiredDeviceId; -static char *requiredProcessName; -static void (*printMessage)(int fd, const char *, size_t); -static void (*printSeparator)(int fd); +static Config_p config = NULL; +static Colors_p colors = NULL; static inline void write_fully(int fd, const char *buffer, size_t length) { @@ -36,6 +26,9 @@ static int find_space_offsets(const char *buffer, size_t length, size_t *space_o { int o = 0; for (size_t i = 16; i < length; i++) { + if (buffer[i] == '\0') + break; + if (buffer[i] == ' ') { space_offsets_out[o++] = i; if (o == 3) { @@ -45,56 +38,142 @@ static int find_space_offsets(const char *buffer, size_t length, size_t *space_o } return o; } + + +/* ! Modifies buffer ! */ +static void parse_process_params(char *buffer, ProcessParams_p process_params_out) { + memset(process_params_out, 0, sizeof(ProcessParams)); + + process_params_out->name = buffer; + + for (int i = strlen(buffer); i != 0; i--) { + if (buffer[i] == ']') + buffer[i] = '\0'; + + if (buffer[i] == '[') { + process_params_out->pid = buffer + i + 1; + buffer[i] = '\0'; + } + + if (buffer[i] == ')') + buffer[i] = '\0'; + + if (buffer[i] == '(') { + process_params_out->extension = buffer + i + 1; + buffer[i] = '\0'; + } + } +} + static unsigned char should_print_message(const char *buffer, size_t length) { if (length < 3) return 0; // don't want blank lines + unsigned char should_print = 1; + size_t space_offsets[3]; find_space_offsets(buffer, length, space_offsets); - // Check whether process name matches the one passed to -p option and filter if needed - if (requiredProcessName != NULL) { + + // Both process name/pid and extension name filters require process params parsing + if (config->requiredProcessNames != NULL || config->requiredExtensionNames != NULL) { int nameLength = space_offsets[1] - space_offsets[0]; //This size includes the NULL terminator. - char *processName = malloc(nameLength); - processName[nameLength - 1] = '\0'; - memcpy(processName, buffer + space_offsets[0] + 1, nameLength - 1); - - for (int i = strlen(processName); i != 0; i--) - if (processName[i] == '[') - processName[i] = '\0'; + char *allProcessParams = malloc(nameLength); + allProcessParams[nameLength - 1] = '\0'; + + memcpy(allProcessParams, buffer + space_offsets[0] + 1, nameLength - 1); + + ProcessParams processParams; + parse_process_params(allProcessParams, &processParams); + + // Check whether process name matches the list passed to -p option and filter if needed + if (config->requiredProcessNames != NULL) { + char *currentProcessName = strtok(strdup(config->requiredProcessNames), ", "); + while (currentProcessName != NULL) { + bool isPID = (processParams.pid != NULL); + for (int i=0; i < strlen(currentProcessName); i++) { + if (isnumber(currentProcessName[i]) == 0) { + isPID = false; + break; + } + } + + should_print = (strcmp((isPID) ? processParams.pid : processParams.name, currentProcessName) == 0); + + if (should_print) + break; + + currentProcessName = strtok(NULL, ", "); + } + + if (!should_print) + return should_print; + } + - if (strcmp(processName, requiredProcessName) != 0){ - free(processName); - return 0; + // Check whether extension name matches the list passed to -e option and filter if needed + if (config->requiredExtensionNames != NULL) { + if (processParams.extension == NULL) + return false; + + char *currentExtensionName = strtok(strdup(config->requiredExtensionNames), ", "); + while (currentExtensionName != NULL) { + should_print = (strcmp(processParams.extension, currentExtensionName) == 0); + + if (should_print) + break; + + currentExtensionName = strtok(NULL, ", "); + } + + if (!should_print) + return should_print; } - free(processName); + + free(allProcessParams); + } + + // Check whether buffer matches the regex passed to -r option and filter if needed + if (config->use_regex) { + char message[length + 1]; + + memcpy(message, buffer, length); + message[length + 1] = '\0'; + + if (regexec(&config->requiredRegex, message, 0, NULL, 0) == REG_NOMATCH) + should_print = false; } + - // More filtering options can be added here and return 0 when they won't meed filter criteria + // More filtering options can be added here and return 0 when they won't meet filter criteria - return 1; + return should_print; } -#define write_const(fd, text) write_fully(fd, text, sizeof(text)-1) +#define write_const(fd, text) write_fully(fd, text, strlen(text)) +#define stringify(x) #x +#define xcode_color_with_rgb(type, r, g, b) "\e[" type stringify(r) "," stringify(g) "," stringify(b) ";" -#define COLOR_RESET "\e[m" -#define COLOR_NORMAL "\e[0m" -#define COLOR_DARK "\e[2m" -#define COLOR_RED "\e[0;31m" -#define COLOR_DARK_RED "\e[2;31m" -#define COLOR_GREEN "\e[0;32m" -#define COLOR_DARK_GREEN "\e[2;32m" -#define COLOR_YELLOW "\e[0;33m" -#define COLOR_DARK_YELLOW "\e[2;33m" -#define COLOR_BLUE "\e[0;34m" -#define COLOR_DARK_BLUE "\e[2;34m" -#define COLOR_MAGENTA "\e[0;35m" -#define COLOR_DARK_MAGENTA "\e[2;35m" -#define COLOR_CYAN "\e[0;36m" -#define COLOR_DARK_CYAN "\e[2;36m" -#define COLOR_WHITE "\e[0;37m" -#define COLOR_DARK_WHITE "\e[0;37m" +static void set_colors(bool xcode_colors) { + colors->reset = (xcode_colors) ? "\e[;" : "\e[m"; + colors->normal = (xcode_colors) ? xcode_color_with_rgb("fg", 0, 0, 0) : "\e[0m"; + colors->dark = (xcode_colors) ? xcode_color_with_rgb("fg", 102, 102, 102) : "\e[2m"; + colors->red = (xcode_colors) ? xcode_color_with_rgb("fg", 151, 4, 12) : "\e[0;31m"; + colors->dark_red = (xcode_colors) ? xcode_color_with_rgb("fg", 227, 10, 23) : "\e[2;31m"; + colors->green = (xcode_colors) ? xcode_color_with_rgb("fg", 23, 164, 26) : "\e[0;32m"; + colors->dark_green = (xcode_colors) ? xcode_color_with_rgb("fg", 33, 215, 38) : "\e[2;32m"; + colors->yellow = (xcode_colors) ? xcode_color_with_rgb("fg", 153, 152, 29) : "\e[0;33m"; + colors->dark_yellow = (xcode_colors) ? xcode_color_with_rgb("fg", 229, 228, 49) : "\e[2;33m"; + colors->blue = (xcode_colors) ? xcode_color_with_rgb("fg", 5, 22, 175) : "\e[0;34m"; + colors->dark_blue = (xcode_colors) ? xcode_color_with_rgb("fg", 11, 36, 251) : "\e[2;34m"; + colors->magenta = (xcode_colors) ? xcode_color_with_rgb("fg", 177, 25, 176) : "\e[0;35m"; + colors->dark_magenta = (xcode_colors) ? xcode_color_with_rgb("fg", 227, 25, 227) : "\e[2;35m"; + colors->cyan = (xcode_colors) ? xcode_color_with_rgb("fg", 26, 166, 177) : "\e[0;36m"; + colors->dark_cyan = (xcode_colors) ? xcode_color_with_rgb("fg", 39, 229, 228) : "\e[2;36m"; + colors->white = (xcode_colors) ? xcode_color_with_rgb("fg", 191, 191, 191) : "\e[0;37m"; + colors->dark_white = (xcode_colors) ? xcode_color_with_rgb("fg", 230, 229, 230) : "\e[0;37m"; +} static void write_colored(int fd, const char *buffer, size_t length) { @@ -108,21 +187,66 @@ static void write_colored(int fd, const char *buffer, size_t length) if (o == 3) { // Log date and device name - write_const(fd, COLOR_DARK_WHITE); + write_const(fd, colors->dark_white); write_fully(fd, buffer, space_offsets[0]); - // Log process name + // Log process name, extension name and pid int pos = 0; - for (int i = space_offsets[0]; i < space_offsets[0]; i++) { + for (int i = space_offsets[0]; i < space_offsets[1]; i++) { + if (buffer[i] == '(') { + pos = i; + break; + } + if (buffer[i] == '[') { pos = i; break; } } - write_const(fd, COLOR_CYAN); - if (pos && buffer[space_offsets[1]-1] == ']') { + write_const(fd, colors->cyan); + if (pos) { + // Process name write_fully(fd, buffer + space_offsets[0], pos - space_offsets[0]); - write_const(fd, COLOR_DARK_CYAN); - write_fully(fd, buffer + pos, space_offsets[1] - pos); + + if (buffer[pos] == '(') { + // Find '[' too + + int new_pos = 0; + for (int i = space_offsets[0]; i < space_offsets[1]; i++) { + if (buffer[i] == '[') { + new_pos = i; + break; + } + } + + if (new_pos) { + // Process extension name + write_const(fd, colors->dark_cyan); + write_fully(fd, buffer + pos, 1); + write_const(fd, colors->cyan); + write_fully(fd, buffer + pos + 1, new_pos - pos - 2); + write_const(fd, colors->dark_cyan); + write_fully(fd, buffer + new_pos - 1, 1); + + // PID + write_const(fd, colors->dark_cyan); + write_fully(fd, buffer + new_pos, 1); + write_const(fd, colors->cyan); + write_fully(fd, buffer + new_pos + 1, space_offsets[1] - new_pos - 2); + write_const(fd, colors->dark_cyan); + write_fully(fd, buffer + space_offsets[1] - 1, 1); + } else { + write_fully(fd, buffer + pos, space_offsets[1] - pos); + } + + } else { + // PID + write_const(fd, colors->dark_cyan); + write_fully(fd, buffer + pos, 1); + write_const(fd, colors->cyan); + write_fully(fd, buffer + pos + 1, space_offsets[1] - pos - 2); + write_const(fd, colors->dark_cyan); + write_fully(fd, buffer + space_offsets[1] - 1, 1); + } } else { write_fully(fd, buffer + space_offsets[0], space_offsets[1] - space_offsets[0]); } @@ -132,17 +256,17 @@ static void write_colored(int fd, const char *buffer, size_t length) const char *normalColor; const char *darkColor; if (levelLength == 9 && memcmp(buffer + space_offsets[1], " :", 9) == 0){ - normalColor = COLOR_MAGENTA; - darkColor = COLOR_DARK_MAGENTA; + normalColor = colors->magenta; + darkColor = colors->dark_magenta; } else if (levelLength == 11 && memcmp(buffer + space_offsets[1], " :", 11) == 0){ - normalColor = COLOR_YELLOW; - darkColor = COLOR_DARK_YELLOW; + normalColor = colors->yellow; + darkColor = colors->dark_yellow; } else if (levelLength == 9 && memcmp(buffer + space_offsets[1], " :", 9) == 0){ - normalColor = COLOR_RED; - darkColor = COLOR_DARK_RED; + normalColor = colors->red; + darkColor = colors->dark_red; } else if (levelLength == 10 && memcmp(buffer + space_offsets[1], " :", 10) == 0) { - normalColor = COLOR_GREEN; - darkColor = COLOR_DARK_GREEN; + normalColor = colors->green; + darkColor = colors->dark_green; } else { goto level_unformatted; } @@ -152,14 +276,14 @@ static void write_colored(int fd, const char *buffer, size_t length) write_fully(fd, buffer + space_offsets[1] + 2, levelLength - 4); write_string(fd, darkColor); write_fully(fd, buffer + space_offsets[1] + levelLength - 2, 1); - write_const(fd, COLOR_DARK_WHITE); + write_const(fd, colors->dark_white); write_fully(fd, buffer + space_offsets[1] + levelLength - 1, 1); } else { level_unformatted: - write_const(fd, COLOR_RESET); + write_const(fd, colors->reset); write_fully(fd, buffer + space_offsets[1], levelLength); } - write_const(fd, COLOR_RESET); + write_const(fd, colors->reset); write_fully(fd, buffer + space_offsets[2], length - space_offsets[2]); } else { write_fully(fd, buffer, length); @@ -181,10 +305,32 @@ static void SocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef a while ((buffer[extentLength] != '\0') && extentLength != length) { extentLength++; } + + static unsigned char should_print_result = false; + bool should_filter = false; + + if ( strnstr(buffer, ":", 150) || strnstr(buffer, ":", 150) + || strnstr(buffer, ":", 150) || strnstr(buffer, ":", 150) + || strnstr(buffer, ":", 150) || strnstr(buffer, ":", 150)) + { + should_filter = true; + } + + if (should_filter) + should_print_result = should_print_message(buffer, extentLength); - if (should_print_message(buffer, extentLength)) { - printMessage(1, buffer, extentLength); - printSeparator(1); + if (should_print_result) { + if (should_filter) { + static bool is_first_message = true; + if (!is_first_message) + config->printSeparator(1); + else + is_first_message = false; + + config->printMessage(1, buffer, extentLength); + } + else + write_fully(1, buffer, extentLength); } length -= extentLength; @@ -197,16 +343,16 @@ static void DeviceNotificationCallback(am_device_notification_callback_info *inf struct am_device *device = info->dev; switch (info->msg) { case ADNCI_MSG_CONNECTED: { - if (debug) { + if (config->debug) { CFStringRef deviceId = AMDeviceCopyDeviceIdentifier(device); CFStringRef str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("deviceconsole connected: %@"), deviceId); CFRelease(deviceId); CFShow(str); CFRelease(str); } - if (requiredDeviceId) { + if (config->requiredDeviceId) { CFStringRef deviceId = AMDeviceCopyDeviceIdentifier(device); - Boolean isRequiredDevice = CFEqual(deviceId, requiredDeviceId); + Boolean isRequiredDevice = CFEqual(deviceId, config->requiredDeviceId); CFRelease(deviceId); if (!isRequiredDevice) break; @@ -226,7 +372,7 @@ static void DeviceNotificationCallback(am_device_notification_callback_info *inf data->connection = connection; data->socket = socket; data->source = source; - CFDictionarySetValue(liveConnections, device, data); + CFDictionarySetValue(config->liveConnections, device, data); return; } CFRelease(source); @@ -240,16 +386,16 @@ static void DeviceNotificationCallback(am_device_notification_callback_info *inf break; } case ADNCI_MSG_DISCONNECTED: { - if (debug) { + if (config->debug) { CFStringRef deviceId = AMDeviceCopyDeviceIdentifier(device); CFStringRef str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("deviceconsole disconnected: %@"), deviceId); CFRelease(deviceId); CFShow(str); CFRelease(str); } - DeviceConsoleConnection *data = (DeviceConsoleConnection *)CFDictionaryGetValue(liveConnections, device); + DeviceConsoleConnection *data = (DeviceConsoleConnection *)CFDictionaryGetValue(config->liveConnections, device); if (data) { - CFDictionaryRemoveValue(liveConnections, device); + CFDictionaryRemoveValue(config->liveConnections, device); AMDeviceRelease(device); CFRunLoopRemoveSource(CFRunLoopGetMain(), data->source, kCFRunLoopCommonModes); CFRelease(data->source); @@ -276,41 +422,92 @@ static void plain_separator(int fd) static void color_separator(int fd) { - write_const(fd, COLOR_DARK_WHITE "--" COLOR_RESET "\n"); + size_t buffer_lentgh = sizeof(char) * (strlen(colors->dark_white) + strlen(colors->reset) + 3); + char *buffer = malloc(buffer_lentgh); + sprintf(buffer, "%s--%s\n", colors->dark_white, colors->reset); + + write_const(fd, buffer); + free(buffer); +} + +static void print_help(char *process_name) { + fprintf(stderr, + "Usage: %s [options]\n" + "Options:\n" + " -d\t\t\t\tInclude connect/disconnect messages in standard out" + "\n" + " -u \t\t\tShow only logs from a specific device" + "\n" + "-p <\"process name, pid\">\tShow only logs from a specific process name or pid" + "\n" + "-e <\"kext name, dylib name\">\tShow only logs from a specific process extension (kext/dylib) - *iOS 10 only*" + "\n" + "-r \tFilter messages by regular expression." + "\n" + "-x\t\t\t\tDisable tty coloring in Xcode (unless XcodeColors intalled)." + "\n" + "\n" + "Control-C to disconnect" + "\n" + "Mail bug reports and suggestions to (or )" + "\n" + , process_name); } int main (int argc, char * const argv[]) { - if ((argc == 2) && (strcmp(argv[1], "--help") == 0)) { - fprintf(stderr, "Usage: %s [options]\nOptions:\n -d\t\t\tInclude connect/disconnect messages in standard out\n -u \t\tShow only logs from a specific device\n -p \tShow only logs from a specific process\n\nControl-C to disconnect\nMail bug reports and suggestions to \n", argv[0]); + if ((argc == 2) && ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0))) { + print_help(argv[0]); return 1; } + + config = malloc(sizeof(Config)); + memset(config, 0, sizeof(Config)); + int c; bool use_separators = false; - bool force_color = false; + bool force_color = isatty(1); + bool xcode_colors = ((getenv("XcodeColors")) ? strstr(getenv("XcodeColors"), "YES") != NULL : false); + bool in_xcode = xcode_colors; - while ((c = getopt(argc, argv, "dcsu:p:")) != -1) + while ((c = getopt(argc, argv, "dcxsu:p:r:e:")) != -1) switch (c) { case 'd': - debug = 1; + config->debug = 1; break; case 'c': force_color = true; break; + case 'x': + in_xcode = true; + break; case 's': use_separators = true; break; case 'u': - if (requiredDeviceId) - CFRelease(requiredDeviceId); - requiredDeviceId = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingASCII); + if (config->requiredDeviceId) + CFRelease(config->requiredDeviceId); + config->requiredDeviceId = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingASCII); break; case 'p': - requiredProcessName = malloc(strlen(optarg) + 1); - requiredProcessName[strlen(optarg)] = '\0'; + config->requiredProcessNames = malloc(strlen(optarg) + 1); + config->requiredProcessNames[strlen(optarg)] = '\0'; - strcpy(requiredProcessName, optarg); + strcpy(config->requiredProcessNames, optarg); + break; + case 'e': + config->requiredExtensionNames = malloc(strlen(optarg) + 1); + config->requiredExtensionNames[strlen(optarg)] = '\0'; + + strcpy(config->requiredExtensionNames, optarg); + break; + case 'r': + config->use_regex = true; + if (regcomp(&config->requiredRegex, optarg, REG_EXTENDED | REG_NEWLINE | REG_ICASE)) { + fprintf(stderr, "Error: Could not compile regex %s.\n", optarg); + return 1; + } break; case '?': if (optopt == 'u') @@ -323,16 +520,35 @@ int main (int argc, char * const argv[]) default: abort(); } - if (force_color || isatty(1)) { - printMessage = &write_colored; - printSeparator = use_separators ? &color_separator : &no_separator; + if ((!in_xcode && force_color) || xcode_colors) { + colors = malloc(sizeof(Colors)); + set_colors(xcode_colors); + + config->printMessage = &write_colored; + config->printSeparator = use_separators ? &color_separator : &no_separator; } else { - printMessage = &write_fully; - printSeparator = use_separators ? &plain_separator : &no_separator; + config->printMessage = &write_fully; + config->printSeparator = use_separators ? &plain_separator : &no_separator; } - liveConnections = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); + config->liveConnections = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); am_device_notification *notification; AMDeviceNotificationSubscribe(DeviceNotificationCallback, 0, 0, NULL, ¬ification); CFRunLoopRun(); + + if (config->requiredProcessNames != NULL) + free(config->requiredProcessNames); + + if (config->requiredExtensionNames != NULL) + free(config->requiredExtensionNames); + + if (config->use_regex) + regfree(&config->requiredRegex); + + if (config != NULL) + free(config); + + if (colors != NULL) + free(colors); + return 0; } diff --git a/main.h b/main.h new file mode 100644 index 0000000..1cb3bb7 --- /dev/null +++ b/main.h @@ -0,0 +1,46 @@ +#include +#include "MobileDevice.h" + +typedef struct { + service_conn_t connection; + CFSocketRef socket; + CFRunLoopSourceRef source; +} DeviceConsoleConnection; + +typedef struct { + char *name; + char *pid; + char *extension; +} ProcessParams, *ProcessParams_p; + +typedef struct { + char *reset; + char *normal; + char *dark; + char *red; + char *dark_red; + char *green; + char *dark_green; + char *yellow; + char *dark_yellow; + char *blue; + char *dark_blue; + char *magenta; + char *dark_magenta; + char *cyan; + char *dark_cyan; + char *white; + char *dark_white; +} Colors, *Colors_p; + +typedef struct { + int debug; + CFMutableDictionaryRef liveConnections; + CFStringRef requiredDeviceId; + char *requiredProcessNames; + char *requiredExtensionNames; + bool use_regex; + regex_t requiredRegex; + void (*printMessage)(int fd, const char *, size_t); + void (*printSeparator)(int fd); +} Config, *Config_p;