idevicesyslog: Add color support and filtering options
diff --git a/docs/idevicesyslog.1 b/docs/idevicesyslog.1
index aa24339..fce1ff5 100644
--- a/docs/idevicesyslog.1
+++ b/docs/idevicesyslog.1
@@ -10,20 +10,106 @@
 Relay syslog of a connected device.
 
 .SH OPTIONS
-.TP 
-.B \-d, \-\-debug
-enable communication debugging.
 .TP
 .B \-u, \-\-udid UDID
-target specific device by UDID.
-.TP 
+target specific device by UDID
+.TP
+.B \-n, \-\-network
+connect to network device even if available via USB
+.TP
+.B \-x, \-\-exit
+exit when device disconnects
+.TP
+.B \-d, \-\-debug
+enable communication debugging
+.TP
 .B \-h, \-\-help
-prints usage information.
+prints usage information
 
-.SH AUTHOR
-Martin Szulecki
+.SH FILTER OPTIONS
+.TP
+.B \-m, \-\-match STRING
+only print messages that contain STRING
+
+This option will set a filter to only printed log messages that contain the given string.
+.TP
+.B \-t, \-\-trigger STRING
+start logging when matching STRING
+
+When specified, logging will start as soon as a log messages is encountered that contains the given string. See also
+\f[B]\-T, \-\-untrigger\f[]. Other filters are still applied but obviously filtered messages are only printed after logging has started.
+.TP
+.B \-T, \-\-untrigger STRING
+stop logging when matching STRING
+
+When specified logging will halt as soon as a log message is encountered that contains the given string. See also
+\f[B]\-t, \-\-trigger\f[]. Other filters are still applied but obviously filtered messages are only printed before logging stops.
+
+NOTE: If no \f[B]\-\-trigger\f[] is given, idevicesyslog will exit after a matching log message was encountered.
+.TP
+.B \-p, \-\-process PROCESS
+only print messages from matching process(es)
+
+PROCESS is a string that can either be a numeric pid or a process name. It also supports multiple process names or pids in one string, separated by | (make sure to use quotes!).
+.TP
+.B \-e, \-\-exclude PROCESS
+print all messages except matching process(es)
+
+PROCESS is a string that can either be a numeric pid or a process name. It also supports multiple process names or pids in one string, separated by | (make sure to use quotes!).
+.TP
+.B \-q, \-\-quiet
+set a filter to exclude common noisy processes
+
+Since the syslog can be quite noisy, this quick command line switch allows to silence out a predefined set of commonly known processes. The list of processes that are silenced can be retrieved with \f[B]\-\-quiet\-list\f[].
+.TP
+.B \-\-quiet\-list
+prints the list of processes for \f[B]\-\-quiet\f[] and exits
+.TP
+.B \-k, \-\-kernel
+only print kernel messages
+
+This is actually equivalent to passing \f[B]\-\-process kernel\f[] with the exception that it can be used with \f[B]\-\-quiet\f[] to silence out the noisy process but still get all the kernel log messages.
+.TP
+.B \-K, \-\-no\-kernel
+suppress kernel messages
+
+This is equivalent to passing \f[B]\-\-exclude kernel\f[].
+
+.SH EXAMPLES
+.TP
+.B idevicesyslog \-u 00008030\-0000111ABC000DEF
+Relay syslog of device with UDID 00008030-0000111ABC000DEF.
+.TP
+.B idevicesyslog \-x
+Relay syslog of device and exit when the device is unplugged.
+.TP
+.B idevicesyslog \-m '####' \-e 'identityservicesd' \-K
+Only print log messages that contain the string #### and do NOT originate from identityservicesd or the kernel.
+.TP
+.B idevicesyslog \-p MyApp \-p ReportCrash
+Only print log messages from the process named 'MyApp' and 'ReportCrash'.
+.TP
+.B idevicesyslog \-p 'MyApp|ReportCrash'
+Same as previous example with different syntax.
+.TP
+.B idevicesyslog \-e 'backboardd|CommCenter|mDNSResponder'
+Suppress log messages from backboardd, CommCenter, and mDNSResponder.
+.TP
+.B idevicesyslog \-q \-k
+Suppress log messages from common noisy processes, but DO print kernel log messages.
+.TP
+.B idevicesyslog \-K
+Suppress log messages from kernel, but print everything else
+.TP
+.B idevicesyslog \-t 'backlight on' \-T 'backlight off' \-q
+Start logging when the device turns on backlight and stop logging when it turns backlight off, and suppress noisy processes
+
+.SH AUTHORS
+Nikias Bassen, Martin Szulecki
 
 Man page written to conform with Debian by Julien Lavergne.
 
 .SH ON THE WEB
-http://libimobiledevice.org
+https://github.com/libimobiledevice/libimobiledevice
+
+https://libimobiledevice.org
diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c
index 619b51b..b3a754f 100644
--- a/tools/idevicesyslog.c
+++ b/tools/idevicesyslog.c
@@ -2,7 +2,7 @@
  * idevicesyslog.c
  * Relay the syslog of a device to stdout
  *
- * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
+ * 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
@@ -42,19 +42,325 @@
 
 static int quit_flag = 0;
 static int exit_on_disconnect = 0;
+static int use_colors = 0;
+static int show_device_name = 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** 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 const char QUIET_FILTER[] = "CommCenter|SpringBoard|UserEventAgent|WirelessRadioManagerd|aggregated|appstored|backboardd|biometrickitd|bluetoothd|callservicesd|contextstored|corespeechd|dasd|gpsd|homed|identityservicesd|itunesstored|kernel|locationd|mDNSResponder|mediaremoted|mediaserverd|navd|nsurlsessiond|powerd|rapportd|routined|runningboardd|sharingd|symptomsd|thermalmonitord|useractivityd|wifid";
+
 enum idevice_options lookup_opts = IDEVICE_LOOKUP_USBMUX | IDEVICE_LOOKUP_NETWORK;
 
+static char *line = NULL;
+static int line_buffer_size = 0;
+static int lp = 0;
+
+#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_BRIGHT_CYAN   "\e[1;36m"
+#define COLOR_DARK_CYAN     "\e[2;36m"
+#define COLOR_WHITE         "\e[1;37m"
+#define COLOR_DARK_WHITE    "\e[0;37m"
+
+#define TEXT_COLOR(x) if (use_colors) { fwrite(x, 1, sizeof(x), stdout); }
+
+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, char* end)
+{
+	while ((**p != c) && (*p < end)) {
+		(*p)++;
+	}
+	return (**p == c);
+}
+
+static void stop_logging(void);
+
 static void syslog_callback(char c, void *user_data)
 {
-	putchar(c);
-	if (c == '\n') {
-		fflush(stdout);
+	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;
+				TEXT_COLOR(COLOR_WHITE);
+				break;
+			} else 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;
+					} else {
+						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 */
+				if (num_msg_filters > 0) {
+					int found = 0;
+					int i;
+					for (i = 0; i < num_msg_filters; i++) {
+						if (strstr(device_name_end+1, msg_filters[i])) {
+							found = 1;
+							break;
+						}
+					}
+					if (!found) {
+						shall_print = 0;
+						break;
+					} else {
+						shall_print = 1;
+					}
+				}
+
+				/* 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++;
+
+				int proc_matched = 0;
+				if (num_pid_filters > 0) {
+					char* endp = NULL;
+					int pid_value = (int)strtol(pid_start, &endp, 10);
+					if (endp && (*endp == ']')) {
+						int found = proc_filter_excluding;
+						int i = 0;
+						for (i = 0; i < num_pid_filters; i++) {
+							if (pid_value == 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_start, process_name_end-process_name_start) == 0) {
+							found = !proc_filter_excluding;
+							break;
+						}
+					}
+					if (found) {
+						proc_matched = 1;
+					}
+				}
+				if (proc_matched) {
+					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, "<Notice>:", 9)) {
+					level_end += 9;
+					level_color = COLOR_GREEN;
+				} else if (!strncmp(p, "<Error>:", 8)) {
+					level_end += 8;
+					level_color = COLOR_RED;
+				} else if (!strncmp(p, "<Warning>:", 10)) {
+					level_end += 10;
+					level_color = COLOR_YELLOW;
+				} else if (!strncmp(p, "<Debug>:", 8)) {
+					level_end += 8;
+					level_color = COLOR_MAGENTA;
+				} else {
+					level_color = COLOR_WHITE;
+				}
+
+				/* write date and time */
+				TEXT_COLOR(COLOR_DARK_WHITE);
+				fwrite(line, 1, 16, stdout);
+
+				if (show_device_name) {
+					/* write device name */
+					TEXT_COLOR(COLOR_DARK_YELLOW);
+					fwrite(device_name_start, 1, device_name_end-device_name_start+1, stdout);
+					TEXT_COLOR(COLOR_RESET);
+				}
+
+				/* write process name */
+				TEXT_COLOR(COLOR_BRIGHT_CYAN);
+				fwrite(process_name_start, 1, process_name_end-process_name_start, stdout);
+				TEXT_COLOR(COLOR_CYAN);
+				fwrite(process_name_end, 1, proc_name_end-process_name_end+1, stdout);
+
+				/* write log level */
+				TEXT_COLOR(level_color);
+				if (level_end > level_start) {
+					fwrite(level_start, 1, level_end-level_start, stdout);
+					p = level_end;
+				}
+
+				lp -= p - linep;
+				linep = p;
+
+				TEXT_COLOR(COLOR_WHITE);
+
+			} else {
+				shall_print = 1;
+				TEXT_COLOR(COLOR_WHITE);
+			}
+		} while (0);
+
+		if ((num_msg_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);
+			TEXT_COLOR(COLOR_RESET);
+			fflush(stdout);
+			if (trigger_off) {
+				triggered = 0;
+			}
+		}
+		line[0] = '\0';
+		lp = 0;
+		return;
 	}
 }
 
@@ -108,7 +414,7 @@
 	}
 
 	/* start capturing syslog */
-	serr = syslog_relay_start_capture(syslog, syslog_callback, NULL);
+	serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL);
 	if (serr != SYSLOG_RELAY_E_SUCCESS) {
 		fprintf(stderr, "ERROR: Unable tot start capturing syslog.\n");
 		syslog_relay_client_free(syslog);
@@ -179,11 +485,25 @@
 	fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
 	fprintf(is_error ? stderr : stdout,
 	  "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 even if available via USB\n" \
+	  "  -x, --exit       exit when device disconnects\n" \
 	  "  -h, --help       prints usage information\n" \
 	  "  -d, --debug      enable communication debugging\n" \
-	  "  -x, --exit       exit when device disconnects\n" \
+	  "\n" \
+	  "FILTER OPTIONS:\n" \
+	  "  -m, --match STRING     only print messages that 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 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" \
+	  "For filter example usage consult idevicesyslog(1) man page.\n" \
 	  "\n" \
 	  "Homepage: <" PACKAGE_URL ">\n"
 	);
@@ -191,6 +511,10 @@
 
 int main(int argc, char *argv[])
 {
+	int include_filter = 0;
+	int exclude_filter = 0;
+	int include_kernel = 0;
+	int exclude_kernel = 0;
 	int c = 0;
 	const struct option longopts[] = {
 		{ "debug", no_argument, NULL, 'd' },
@@ -198,6 +522,15 @@
 		{ "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 },
 		{ NULL, 0, NULL, 0}
 	};
 
@@ -208,7 +541,7 @@
 	signal(SIGPIPE, SIG_IGN);
 #endif
 
-	while ((c = getopt_long(argc, argv, "dhu:nx", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:e:p:qkK", longopts, NULL)) != -1) {
 		switch (c) {
 		case 'd':
 			idevice_set_debug_level(1);
@@ -225,21 +558,143 @@
 		case 'n':
 			lookup_opts |= IDEVICE_LOOKUP_PREFER_NETWORK;
 			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 '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;
+		}
 		default:
 			print_usage(argc, argv, 1);
 			return 2;
 		}
 	}
 
+	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;
+	} else 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 (isatty(1)) {
+		use_colors = 1;
+	}
+
 	int num = 0;
 	idevice_info_t *devices = NULL;
 	idevice_get_device_list_extended(&devices, &num);
@@ -253,6 +708,9 @@
 		}
 	}
 
+	line_buffer_size = 1024;
+	line = malloc(line_buffer_size);
+
 	idevice_event_subscribe(device_event_cb, NULL);
 
 	while (!quit_flag) {
@@ -261,6 +719,40 @@
 	idevice_event_unsubscribe();
 	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_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;