summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am35
-rw-r--r--tools/afcclient.c1630
-rw-r--r--tools/idevicebackup.c153
-rw-r--r--tools/idevicebackup2.c107
-rw-r--r--tools/idevicebtlogger.c4
-rw-r--r--tools/idevicecrashreport.c148
-rw-r--r--tools/idevicedate.c6
-rw-r--r--tools/idevicedebug.c9
-rw-r--r--tools/idevicedebugserverproxy.c37
-rw-r--r--tools/idevicedevmodectl.c462
-rw-r--r--tools/idevicediagnostics.c4
-rw-r--r--tools/ideviceenterrecovery.c4
-rw-r--r--tools/ideviceimagemounter.c485
-rw-r--r--tools/ideviceinfo.c10
-rw-r--r--tools/idevicename.c4
-rw-r--r--tools/idevicenotificationproxy.c26
-rw-r--r--tools/idevicepair.c11
-rw-r--r--tools/ideviceprovision.c37
-rw-r--r--tools/idevicescreenshot.c4
-rw-r--r--tools/idevicesetlocation.c30
-rw-r--r--tools/idevicesyslog.c740
21 files changed, 3443 insertions, 503 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index e8ef3ab..24cfc66 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -4,6 +4,7 @@ AM_CPPFLAGS = \
AM_CFLAGS = \
$(GLOBAL_CFLAGS) \
+ $(ssl_lib_CFLAGS) \
$(libplist_CFLAGS) \
$(LFS_CFLAGS)
@@ -27,12 +28,14 @@ bin_PROGRAMS = \
idevicedebugserverproxy \
idevicediagnostics \
idevicedebug \
+ idevicedevmodectl \
idevicenotificationproxy \
idevicecrashreport \
- idevicesetlocation
+ idevicesetlocation \
+ afcclient
idevicebtlogger_SOURCES = idevicebtlogger.c
-iidevicebtlogger_CFLAGS = $(AM_CFLAGS)
+idevicebtlogger_CFLAGS = $(AM_CFLAGS)
idevicebtlogger_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
idevicebtlogger_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
@@ -47,9 +50,9 @@ idevicename_LDFLAGS = $(AM_LDFLAGS)
idevicename_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicepair_SOURCES = idevicepair.c
-idevicepair_CFLAGS = $(AM_CFLAGS) $(ssl_lib_CFLAGS)
-idevicepair_LDFLAGS = $(AM_LDFLAGS) $(libusbmuxd_LIBS) $(ssl_lib_LIBS)
-idevicepair_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la $(limd_glue_LIBS)
+idevicepair_CFLAGS = $(AM_CFLAGS)
+idevicepair_LDFLAGS = $(AM_LDFLAGS) $(libusbmuxd_LIBS)
+idevicepair_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la $(limd_glue_LIBS) $(ssl_lib_LIBS)
idevicesyslog_SOURCES = idevicesyslog.c
idevicesyslog_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
@@ -62,8 +65,8 @@ idevice_id_LDFLAGS = $(AM_LDFLAGS)
idevice_id_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicebackup_SOURCES = idevicebackup.c
-idevicebackup_CFLAGS = $(AM_CFLAGS) $(ssl_lib_CFLAGS) $(limd_glue_CFLAGS)
-idevicebackup_LDFLAGS = $(AM_LDFLAGS) $(ssl_lib_LIBS) $(limd_glue_LIBS)
+idevicebackup_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+idevicebackup_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
idevicebackup_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicebackup2_SOURCES = idevicebackup2.c
@@ -72,8 +75,8 @@ idevicebackup2_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
ideviceimagemounter_SOURCES = ideviceimagemounter.c
-ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
-ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) $(libtatsu_CFLAGS)
+ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) $(libtatsu_LIBS)
ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicescreenshot_SOURCES = idevicescreenshot.c
@@ -111,6 +114,11 @@ idevicedebug_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
idevicedebug_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
idevicedebug_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la
+idevicedevmodectl_SOURCES = idevicedevmodectl.c
+idevicedevmodectl_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+idevicedevmodectl_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+idevicedevmodectl_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la
+
idevicenotificationproxy_SOURCES = idevicenotificationproxy.c
idevicenotificationproxy_CFLAGS = $(AM_CFLAGS)
idevicenotificationproxy_LDFLAGS = $(AM_LDFLAGS)
@@ -125,3 +133,12 @@ idevicesetlocation_SOURCES = idevicesetlocation.c
idevicesetlocation_CFLAGS = $(AM_CFLAGS)
idevicesetlocation_LDFLAGS = $(AM_LDFLAGS)
idevicesetlocation_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
+
+afcclient_SOURCES = afcclient.c
+afcclient_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+afcclient_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+if HAVE_READLINE
+ afcclient_CFLAGS += $(readline_CFLAGS)
+ afcclient_LDFLAGS += $(readline_LIBS)
+endif
+afcclient_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(limd_glue_LIBS)
diff --git a/tools/afcclient.c b/tools/afcclient.c
new file mode 100644
index 0000000..a958c23
--- /dev/null
+++ b/tools/afcclient.c
@@ -0,0 +1,1630 @@
+/*
+ * afcclient.c
+ * Utility to interact with AFC/HoustArrest service on the device
+ *
+ * Inspired by https://github.com/emonti/afcclient
+ * But entirely rewritten from scratch.
+ *
+ * Copyright (c) 2023 Nikias Bassen, 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 "afcclient"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <sys/time.h>
+#include <conio.h>
+#define sleep(x) Sleep(x*1000)
+#define S_IFMT 0170000 /* [XSI] type of file mask */
+#define S_IFIFO 0010000 /* [XSI] named pipe (fifo) */
+#define S_IFCHR 0020000 /* [XSI] character special */
+#define S_IFBLK 0060000 /* [XSI] block special */
+#define S_IFLNK 0120000 /* [XSI] symbolic link */
+#define S_IFSOCK 0140000 /* [XSI] socket */
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /* block special */
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /* char special */
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* fifo or socket */
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* symbolic link */
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* socket */
+#else
+#include <sys/time.h>
+#include <termios.h>
+#endif
+
+#ifdef HAVE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#include <libimobiledevice/libimobiledevice.h>
+#include <libimobiledevice/lockdown.h>
+#include <libimobiledevice/house_arrest.h>
+#include <libimobiledevice/afc.h>
+#include <plist/plist.h>
+
+#include <libimobiledevice-glue/termcolors.h>
+#include <libimobiledevice-glue/utils.h>
+
+#undef st_mtime
+#undef st_birthtime
+struct afc_file_stat {
+ uint16_t st_mode;
+ uint16_t st_nlink;
+ uint64_t st_size;
+ uint64_t st_mtime;
+ uint64_t st_birthtime;
+ uint32_t st_blocks;
+};
+
+static char* udid = NULL;
+static int connected = 0;
+static int use_network = 0;
+static idevice_subscription_context_t context = NULL;
+static char* curdir = NULL;
+static size_t curdir_len = 0;
+
+static int file_exists(const char* path)
+{
+ struct stat tst;
+#ifdef _WIN32
+ return (stat(path, &tst) == 0);
+#else
+ return (lstat(path, &tst) == 0);
+#endif
+}
+
+static int is_directory(const char* path)
+{
+ struct stat tst;
+#ifdef _WIN32
+ return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
+#else
+ return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
+#endif
+}
+
+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"
+ "Interact with AFC/HouseArrest service on a connected device.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -n, --network connect to network device (not recommended!)\n"
+ " --container <appid> Access container of given app\n"
+ " --documents <appid> Access Documents directory of given app\n"
+ " -h, --help prints usage information\n" \
+ " -d, --debug enable communication debugging\n" \
+ " -v, --version prints version information\n" \
+ "\n"
+ );
+ fprintf(is_error ? stderr : stdout,
+ "\n" \
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
+}
+
+#ifndef HAVE_READLINE
+#ifdef _WIN32
+#define BS_CC '\b'
+#else
+#define BS_CC 0x7f
+#define getch getchar
+#endif
+static void get_input(char *buf, int maxlen)
+{
+ int len = 0;
+ int c;
+
+ while ((c = getch())) {
+ if ((c == '\r') || (c == '\n')) {
+ break;
+ }
+ if (isprint(c)) {
+ if (len < maxlen-1)
+ buf[len++] = c;
+ } else if (c == BS_CC) {
+ if (len > 0) {
+ fputs("\b \b", stdout);
+ len--;
+ }
+ }
+ }
+ buf[len] = 0;
+}
+#endif
+
+#define OPT_DOCUMENTS 1
+#define OPT_CONTAINER 2
+
+int stop_requested = 0;
+
+static void handle_signal(int sig)
+{
+ stop_requested++;
+#ifdef _WIN32
+ GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
+#else
+ kill(getpid(), SIGINT);
+#endif
+}
+
+static void handle_help(afc_client_t afc, int argc, char** argv)
+{
+ printf("Available commands:\n");
+ printf("help - print list of available commands\n");
+ printf("devinfo - print device information\n");
+ printf("info PATH - print file attributes of file at PATH\n");
+ printf("ls [-l] PATH - print directory contents of PATH\n");
+ printf("mv OLD NEW - rename file OLD to NEW\n");
+ printf("mkdir PATH - create directory at PATH\n");
+ printf("ln [-s] FILE [LINK] - create a (symbolic) link to file named LINKNAME\n");
+ printf(" NOTE: This feature has been disabled in newer versions of iOS.\n");
+ printf("rm PATH - remove item at PATH\n");
+ printf("get [-rf] PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n");
+ printf("put [-rf] LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n");
+ printf("\n");
+}
+
+static const char* path_get_basename(const char* path)
+{
+ const char *p = strrchr(path, '/');
+ return p ? p + 1 : path;
+}
+
+static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y)
+{
+ /* Perform the carry for the later subtraction by updating y. */
+ if (x->tv_usec < y->tv_usec) {
+ int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
+ y->tv_usec -= 1000000 * nsec;
+ y->tv_sec += nsec;
+ }
+ if (x->tv_usec - y->tv_usec > 1000000) {
+ int nsec = (x->tv_usec - y->tv_usec) / 1000000;
+ y->tv_usec += 1000000 * nsec;
+ y->tv_sec -= nsec;
+ }
+ /* Compute the time remaining to wait.
+ tv_usec is certainly positive. */
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_usec = x->tv_usec - y->tv_usec;
+ /* Return 1 if result is negative. */
+ return x->tv_sec < y->tv_sec;
+}
+
+struct str_item {
+ size_t len;
+ char* str;
+};
+
+static char* get_absolute_path(const char *path)
+{
+ if (*path == '/') {
+ return strdup(path);
+ } else {
+ size_t len = curdir_len + 1 + strlen(path) + 1;
+ char* result = (char*)malloc(len);
+ if (!strcmp(curdir, "/")) {
+ snprintf(result, len, "/%s", path);
+ } else {
+ snprintf(result, len, "%s/%s", curdir, path);
+ }
+ return result;
+ }
+}
+
+static char* get_realpath(const char* path)
+{
+ if (!path) return NULL;
+
+ int is_absolute = 0;
+ if (*path == '/') {
+ is_absolute = 1;
+ }
+
+ const char* p = path;
+ if (is_absolute) {
+ while (*p == '/') p++;
+ }
+ if (*p == '\0') {
+ return strdup("/");
+ }
+
+ int c_count = 1;
+ const char* start = p;
+ const char* end = p;
+ struct str_item* comps = NULL;
+
+ while (*p) {
+ if (*p == '/') {
+ p++;
+ end = p-1;
+ while (*p == '/') p++;
+ if (*p == '\0') break;
+ struct str_item* newcomps = (struct str_item*)realloc(comps, sizeof(struct str_item)*c_count);
+ if (!newcomps) {
+ free(comps);
+ printf("%s: out of memory?!\n", __func__);
+ return NULL;
+ }
+ comps = newcomps;
+ char *comp = (char*)malloc(end-start+1);
+ strncpy(comp, start, end-start);
+ comp[end-start] = '\0';
+ comps[c_count-1].len = end-start;
+ comps[c_count-1].str = comp;
+ c_count++;
+ start = p;
+ end = p;
+ }
+ p++;
+ }
+ if (p > start) {
+ if (start == end) {
+ end = p;
+ }
+ struct str_item* newcomps = (struct str_item*)realloc(comps, sizeof(struct str_item)*c_count);
+ if (!newcomps) {
+ free(comps);
+ printf("%s: out of memory?!\n", __func__);
+ return NULL;
+ }
+ comps = newcomps;
+ char *comp = (char*)malloc(end-start+1);
+ strncpy(comp, start, end-start);
+ comp[end-start] = '\0';
+ comps[c_count-1].len = end-start;
+ comps[c_count-1].str = comp;
+ }
+
+ struct str_item* comps_final = (struct str_item*)malloc(sizeof(struct str_item)*(c_count+1));
+ int o = 1;
+ if (is_absolute) {
+ comps_final[0].len = 1;
+ comps_final[0].str = (char*)"/";
+ } else {
+ comps_final[0].len = curdir_len;
+ comps_final[0].str = curdir;
+ }
+ size_t o_len = comps_final[0].len;
+
+ for (int i = 0; i < c_count; i++) {
+ if (!strcmp(comps[i].str, "..")) {
+ o--;
+ continue;
+ } else if (!strcmp(comps[i].str, ".")) {
+ continue;
+ }
+ o_len += comps[i].len;
+ comps_final[o].str = comps[i].str;
+ comps_final[o].len = comps[i].len;
+ o++;
+ }
+
+ o_len += o;
+ char* result = (char*)malloc(o_len);
+ char* presult = result;
+ for (int i = 0; i < o; i++) {
+ if (i > 0 && strcmp(comps_final[i-1].str, "/") != 0) {
+ *presult = '/';
+ presult++;
+ }
+ strncpy(presult, comps_final[i].str, comps_final[i].len);
+ presult+=comps_final[i].len;
+ *presult = '\0';
+ }
+ if (presult == result) {
+ *presult = '/';
+ presult++;
+ *presult = 0;
+ }
+
+ for (int i = 0; i < c_count; i++) {
+ free(comps[i].str);
+ }
+ free(comps);
+ free(comps_final);
+
+ return result;
+}
+
+static void handle_devinfo(afc_client_t afc, int argc, char** argv)
+{
+ plist_t info = NULL;
+ afc_error_t err = afc_get_device_info_plist(afc, &info);
+ if (err == AFC_E_SUCCESS && info) {
+ if (argc > 0 && !strcmp(argv[0], "--plain")) {
+ plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE);
+ } else {
+ plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE);
+ }
+ } else {
+ printf("Error: Failed to get device info: %s (%d)\n", afc_strerror(err), err);
+ }
+ plist_free(info);
+}
+
+static int get_file_info_stat(afc_client_t afc, const char* path, struct afc_file_stat *stbuf)
+{
+ plist_t info = NULL;
+ afc_error_t ret = afc_get_file_info_plist(afc, path, &info);
+ memset(stbuf, 0, sizeof(struct afc_file_stat));
+ if (ret != AFC_E_SUCCESS) {
+ return -1;
+ } else if (!info) {
+ return -1;
+ }
+ stbuf->st_size = plist_dict_get_uint(info, "st_size");
+ stbuf->st_blocks = plist_dict_get_uint(info, "st_blocks");
+ const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ if (s_ifmt) {
+ if (!strcmp(s_ifmt, "S_IFREG")) {
+ stbuf->st_mode = S_IFREG;
+ } else if (!strcmp(s_ifmt, "S_IFDIR")) {
+ stbuf->st_mode = S_IFDIR;
+ } else if (!strcmp(s_ifmt, "S_IFLNK")) {
+ stbuf->st_mode = S_IFLNK;
+ } else if (!strcmp(s_ifmt, "S_IFBLK")) {
+ stbuf->st_mode = S_IFBLK;
+ } else if (!strcmp(s_ifmt, "S_IFCHR")) {
+ stbuf->st_mode = S_IFCHR;
+ } else if (!strcmp(s_ifmt, "S_IFIFO")) {
+ stbuf->st_mode = S_IFIFO;
+ } else if (!strcmp(s_ifmt, "S_IFSOCK")) {
+ stbuf->st_mode = S_IFSOCK;
+ }
+ }
+ stbuf->st_nlink = plist_dict_get_uint(info, "st_nlink");
+ stbuf->st_mtime = (time_t)(plist_dict_get_uint(info, "st_mtime") / 1000000000);
+ /* available on iOS 7+ */
+ stbuf->st_birthtime = (time_t)(plist_dict_get_uint(info, "st_birthtime") / 1000000000);
+ plist_free(info);
+ return 0;
+}
+
+static void handle_file_info(afc_client_t afc, int argc, char** argv)
+{
+ if (argc < 1) {
+ printf("Error: Missing PATH.\n");
+ return;
+ }
+
+ plist_t info = NULL;
+ char* abspath = get_absolute_path(argv[0]);
+ if (!abspath) {
+ printf("Error: Invalid argument\n");
+ return;
+ }
+ afc_error_t err = afc_get_file_info_plist(afc, abspath, &info);
+ if (err == AFC_E_SUCCESS && info) {
+ if (argc > 1 && !strcmp(argv[1], "--plain")) {
+ plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE);
+ } else {
+ plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE);
+ }
+ } else {
+ printf("Error: Failed to get file info for %s: %s (%d)\n", argv[0], afc_strerror(err), err);
+ }
+ plist_free(info);
+ free(abspath);
+}
+
+static void print_file_info(afc_client_t afc, const char* path, int list_verbose)
+{
+ struct afc_file_stat st;
+ get_file_info_stat(afc, path, &st);
+ if (list_verbose) {
+ char timebuf[64];
+ time_t t = st.st_mtime;
+ if (S_ISDIR(st.st_mode)) {
+ printf("drwxr-xr-x");
+ } else if (S_ISLNK(st.st_mode)) {
+ printf("lrwxrwxrwx");
+ } else {
+ if (S_ISFIFO(st.st_mode)) {
+ printf("f");
+ } else if (S_ISBLK(st.st_mode)) {
+ printf("b");
+ } else if (S_ISCHR(st.st_mode)) {
+ printf("c");
+ } else if (S_ISSOCK(st.st_mode)) {
+ printf("s");
+ } else {
+ printf("-");
+ }
+ printf("rw-r--r--");
+ }
+ printf(" ");
+ printf("%4d", st.st_nlink);
+ printf(" ");
+ printf("mobile");
+ printf(" ");
+ printf("mobile");
+ printf(" ");
+ printf("%10lld", (long long)st.st_size);
+ printf(" ");
+#ifdef _WIN32
+ strftime(timebuf, 64, "%d %b %Y %H:%M:%S", localtime(&t));
+#else
+ strftime(timebuf, 64, "%d %h %Y %H:%M:%S", localtime(&t));
+#endif
+ printf("%s", timebuf);
+ printf(" ");
+ }
+ if (S_ISDIR(st.st_mode)) {
+ cprintf(FG_CYAN);
+ } else if (S_ISLNK(st.st_mode)) {
+ cprintf(FG_MAGENTA);
+ } else if (S_ISREG(st.st_mode)) {
+ cprintf(FG_DEFAULT);
+ } else {
+ cprintf(FG_YELLOW);
+ }
+ cprintf("%s" COLOR_RESET "\n", path_get_basename(path));
+}
+
+static void handle_list(afc_client_t afc, int argc, char** argv)
+{
+ const char* path = NULL;
+ int list_verbose = 0;
+ if (argc < 1) {
+ path = curdir;
+ } else {
+ if (!strcmp(argv[0], "-l")) {
+ list_verbose = 1;
+ if (argc == 2) {
+ path = argv[1];
+ } else {
+ path = curdir;
+ }
+ } else {
+ path = argv[0];
+ }
+ }
+ char* abspath = get_absolute_path(path);
+ if (!abspath) {
+ printf("Error: Invalid argument\n");
+ return;
+ }
+ int abspath_is_root = strcmp(abspath, "/") == 0;
+ size_t abspath_len = (abspath_is_root) ? 0 : strlen(abspath);
+ char** entries = NULL;
+ afc_error_t err = afc_read_directory(afc, abspath, &entries);
+ if (err == AFC_E_READ_ERROR) {
+ print_file_info(afc, abspath, list_verbose);
+ return;
+ } else if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to list '%s': %s (%d)\n", path, afc_strerror(err), err);
+ free(abspath);
+ return;
+ }
+
+ char** p = entries;
+ while (p && *p) {
+ if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
+ p++;
+ continue;
+ }
+ size_t len = abspath_len + 1 + strlen(*p) + 1;
+ char* testpath = (char*)malloc(len);
+ if (abspath_is_root) {
+ snprintf(testpath, len, "/%s", *p);
+ } else {
+ snprintf(testpath, len, "%s/%s", abspath, *p);
+ }
+ print_file_info(afc, testpath, list_verbose);
+ free(testpath);
+ p++;
+ }
+ afc_dictionary_free(entries);
+ free(abspath);
+}
+
+static void handle_rename(afc_client_t afc, int argc, char** argv)
+{
+ if (argc != 2) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+ char* srcpath = get_absolute_path(argv[0]);
+ if (!srcpath) {
+ printf("Error: Invalid argument\n");
+ return;
+ }
+ char* dstpath = get_absolute_path(argv[1]);
+ if (!dstpath) {
+ free(srcpath);
+ printf("Error: Invalid argument\n");
+ return;
+ }
+ afc_error_t err = afc_rename_path(afc, srcpath, dstpath);
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to rename '%s' -> '%s': %s (%d)\n", argv[0], argv[1], afc_strerror(err), err);
+ }
+ free(srcpath);
+ free(dstpath);
+}
+
+static void handle_mkdir(afc_client_t afc, int argc, char** argv)
+{
+ for (int i = 0; i < argc; i++) {
+ char* abspath = get_absolute_path(argv[i]);
+ if (!abspath) {
+ printf("Error: Invalid argument '%s'\n", argv[i]);
+ continue;
+ }
+ afc_error_t err = afc_make_directory(afc, abspath);
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to create directory '%s': %s (%d)\n", argv[i], afc_strerror(err), err);
+ }
+ free(abspath);
+ }
+}
+
+static void handle_link(afc_client_t afc, int argc, char** argv)
+{
+ if (argc < 2) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+ afc_link_type_t link_type = AFC_HARDLINK;
+ if (!strcmp(argv[0], "-s")) {
+ argc--;
+ argv++;
+ link_type = AFC_SYMLINK;
+ }
+ if (argc < 1 || argc > 2) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+ const char *link_name = (argc == 1) ? path_get_basename(argv[0]) : argv[1];
+ char* abs_link_name = get_absolute_path(link_name);
+ if (!abs_link_name) {
+ printf("Error: Invalid argument\n");
+ return;
+ }
+ afc_error_t err = afc_make_link(afc, link_type, argv[0], link_name);
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to create %s link for '%s' at '%s': %s (%d)\n", (link_type == AFC_HARDLINK) ? "hard" : "symbolic", argv[0], link_name, afc_strerror(err), err);
+ }
+}
+
+static int ask_yesno(const char* prompt)
+{
+ int ret = 0;
+#ifdef HAVE_READLINE
+ char* result = readline(prompt);
+ if (result && result[0] == 'y') {
+ ret = 1;
+ }
+#else
+ char cmdbuf[2] = {0, };
+ printf("%s", prompt);
+ fflush(stdout);
+ get_input(cmdbuf, sizeof(cmdbuf));
+ if (cmdbuf[0] == 'y') {
+ ret = 1;
+ }
+#endif
+#ifdef HAVE_READLINE
+ free(result);
+#endif
+ return ret;
+}
+
+static void handle_remove(afc_client_t afc, int argc, char** argv)
+{
+ int recursive = 0;
+ int force = 0;
+ int i = 0;
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--")) {
+ i++;
+ break;
+ } else if (!strcmp(argv[i], "-r")) {
+ recursive = 1;
+ } else if (!strcmp(argv[i], "-f")) {
+ force = 1;
+ } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
+ recursive = 1;
+ force = 1;
+ } else {
+ break;
+ }
+ }
+ if (recursive && !force) {
+ if (!ask_yesno("WARNING: This operation will remove all contents of the given path(s). Continue? [y/N] ")) {
+ printf("Aborted.\n");
+ return;
+ }
+ }
+ for ( ; i < argc; i++) {
+ char* abspath = get_absolute_path(argv[i]);
+ if (!abspath) {
+ printf("Error: Invalid argument '%s'\n", argv[i]);
+ continue;
+ }
+ afc_error_t err;
+ if (recursive) {
+ err = afc_remove_path_and_contents(afc, abspath);
+ } else {
+ err = afc_remove_path(afc, abspath);
+ }
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to remove '%s': %s (%d)\n", argv[i], afc_strerror(err), err);
+ }
+ free(abspath);
+ }
+}
+
+static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint64_t file_size, uint8_t force_overwrite)
+{
+ uint64_t fh = 0;
+ afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh);
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to open file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
+ return 0;
+ }
+ if (file_exists(dstpath) && !force_overwrite) {
+ printf("Error: Failed to overwrite existing file without '-f' option: %s\n", dstpath);
+ return 0;
+ }
+ FILE *f = fopen(dstpath, "wb");
+ if (!f) {
+ printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno));
+ return 0;
+ }
+ struct timeval t1;
+ struct timeval t2;
+ struct timeval tdiff;
+ size_t bufsize = 0x100000;
+ char *buf = malloc(bufsize);
+ size_t total = 0;
+ int progress = 0;
+ int lastprog = 0;
+ if (file_size > 0x400000) {
+ progress = 1;
+ gettimeofday(&t1, NULL);
+ }
+ uint8_t succeed = 1;
+ while (err == AFC_E_SUCCESS) {
+ uint32_t bytes_read = 0;
+ size_t chunk = 0;
+ err = afc_file_read(afc, fh, buf, bufsize, &bytes_read);
+ if (bytes_read == 0) {
+ break;
+ }
+ while (chunk < bytes_read) {
+ size_t wr = fwrite(buf + chunk, 1, bytes_read - chunk, f);
+ if (wr == 0) {
+ if (progress) {
+ printf("\n");
+ }
+ printf("Error: Failed to write to local file\n");
+ succeed = 0;
+ break;
+ }
+ chunk += wr;
+ }
+ total += chunk;
+ if (progress) {
+ int prog = (int) ((double) total / (double) file_size * 100.0f);
+ if (prog > lastprog) {
+ gettimeofday(&t2, NULL);
+ timeval_subtract(&tdiff, &t2, &t1);
+ double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
+ printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
+ fflush(stdout);
+ lastprog = prog;
+ }
+ }
+ }
+ if (progress) {
+ printf("\n");
+ }
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
+ succeed = 0;
+ }
+ free(buf);
+ fclose(f);
+ afc_file_close(afc, fh);
+ return succeed;
+}
+
+static int __mkdir(const char* path)
+{
+#ifdef _WIN32
+ return mkdir(path);
+#else
+ return mkdir(path, 0755);
+#endif
+}
+
+static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_get)
+{
+ plist_t info = NULL;
+ uint64_t file_size = 0;
+ afc_error_t err = afc_get_file_info_plist(afc, srcpath, &info);
+ if (err == AFC_E_OBJECT_NOT_FOUND) {
+ printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
+ return 0;
+ }
+ uint8_t is_dir = 0;
+ if (info) {
+ file_size = plist_dict_get_uint(info, "st_size");
+ const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
+ plist_free(info);
+ }
+ uint8_t succeed = 1;
+ if (is_dir) {
+ if (!recursive_get) {
+ printf("Error: Failed to get a directory without '-r' option: %s\n", srcpath);
+ return 0;
+ }
+ char **entries = NULL;
+ err = afc_read_directory(afc, srcpath, &entries);
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
+ return 0;
+ }
+ char **p = entries;
+ size_t srcpath_len = strlen(srcpath);
+ uint8_t srcpath_is_root = strcmp(srcpath, "/") == 0;
+ // if directory exists, check force_overwrite flag
+ if (is_directory(dstpath)) {
+ if (!force_overwrite) {
+ printf("Error: Failed to write into existing directory without '-f': %s\n", dstpath);
+ return 0;
+ }
+ } else if (__mkdir(dstpath) != 0) {
+ printf("Error: Failed to create directory '%s': %s\n", dstpath, strerror(errno));
+ afc_dictionary_free(entries);
+ return 0;
+ }
+ while (p && *p) {
+ if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
+ p++;
+ continue;
+ }
+ size_t len = srcpath_is_root ? (strlen(*p) + 2) : (srcpath_len + 1 + strlen(*p) + 1);
+ char *testpath = (char *) malloc(len);
+ if (srcpath_is_root) {
+ snprintf(testpath, len, "/%s", *p);
+ } else {
+ snprintf(testpath, len, "%s/%s", srcpath, *p);
+ }
+ uint8_t dst_is_root = strcmp(srcpath, "/") == 0;
+ size_t dst_len = dst_is_root ? (strlen(*p) + 2) : (strlen(dstpath) + 1 + strlen(*p) + 1);
+ char *newdst = (char *) malloc(dst_len);
+ if (dst_is_root) {
+ snprintf(newdst, dst_len, "/%s", *p);
+ } else {
+ snprintf(newdst, dst_len, "%s/%s", dstpath, *p);
+ }
+ if (!get_file(afc, testpath, newdst, force_overwrite, recursive_get)) {
+ succeed = 0;
+ break;
+ }
+ free(testpath);
+ free(newdst);
+ p++;
+ }
+ afc_dictionary_free(entries);
+ } else {
+ succeed = get_single_file(afc, srcpath, dstpath, file_size, force_overwrite);
+ }
+ return succeed;
+}
+
+static void handle_get(afc_client_t afc, int argc, char **argv)
+{
+ if (argc < 1) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+ uint8_t force_overwrite = 0, recursive_get = 0;
+ char *srcpath = NULL;
+ char *dstpath = NULL;
+ int i = 0;
+ for ( ; i < argc; i++) {
+ if (!strcmp(argv[i], "--")) {
+ i++;
+ break;
+ } else if (!strcmp(argv[i], "-r")) {
+ recursive_get = 1;
+ } else if (!strcmp(argv[i], "-f")) {
+ force_overwrite = 1;
+ } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
+ recursive_get = 1;
+ force_overwrite = 1;
+ } else {
+ break;
+ }
+ }
+ if (argc - i == 1) {
+ char *tmp = strdup(argv[i]);
+ size_t src_len = strlen(tmp);
+ if (src_len > 1 && tmp[src_len - 1] == '/') {
+ tmp[src_len - 1] = '\0';
+ }
+ srcpath = get_absolute_path(tmp);
+ dstpath = strdup(path_get_basename(tmp));
+ free(tmp);
+ } else if (argc - i == 2) {
+ char *tmp = strdup(argv[i]);
+ size_t src_len = strlen(tmp);
+ if (src_len > 1 && tmp[src_len - 1] == '/') {
+ tmp[src_len - 1] = '\0';
+ }
+ srcpath = get_absolute_path(tmp);
+ dstpath = strdup(argv[i + 1]);
+ size_t dst_len = strlen(dstpath);
+ if (dst_len > 1 && dstpath[dst_len - 1] == '/') {
+ dstpath[dst_len - 1] = '\0';
+ }
+ free(tmp);
+ } else {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+
+ // target is a directory, put file under this target
+ if (is_directory(dstpath)) {
+ const char *basen = path_get_basename(srcpath);
+ uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
+ size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1);
+ char *newdst = (char *) malloc(len);
+ if (dst_is_root) {
+ snprintf(newdst, len, "/%s", basen);
+ } else {
+ snprintf(newdst, len, "%s/%s", dstpath, basen);
+ }
+ get_file(afc, srcpath, newdst, force_overwrite, recursive_get);
+ free(srcpath);
+ free(newdst);
+ free(dstpath);
+ } else {
+ // target is not a dir or does not exist, just try to create or rewrite it
+ get_file(afc, srcpath, dstpath, force_overwrite, recursive_get);
+ free(srcpath);
+ free(dstpath);
+ }
+}
+
+static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite)
+{
+ plist_t info = NULL;
+ afc_error_t ret = afc_get_file_info_plist(afc, dstpath, &info);
+ // file exists, only overwrite with '-f' option was set
+ if (ret == AFC_E_SUCCESS && info) {
+ plist_free(info);
+ if (!force_overwrite) {
+ printf("Error: Failed to write into existing file without '-f' option: %s\n", dstpath);
+ return 0;
+ }
+ }
+ FILE *f = fopen(srcpath, "rb");
+ if (!f) {
+ printf("Error: Failed to open local file '%s': %s\n", srcpath, strerror(errno));
+ return 0;
+ }
+ struct timeval t1;
+ struct timeval t2;
+ struct timeval tdiff;
+ struct stat fst;
+ int progress = 0;
+ size_t bufsize = 0x100000;
+ char *buf = malloc(bufsize);
+
+ fstat(fileno(f), &fst);
+ if (fst.st_size >= 0x400000) {
+ progress = 1;
+ gettimeofday(&t1, NULL);
+ }
+ size_t total = 0;
+ int lastprog = 0;
+ uint64_t fh = 0;
+ afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh);
+ uint8_t succeed = 1;
+ while (err == AFC_E_SUCCESS) {
+ uint32_t bytes_read = fread(buf, 1, bufsize, f);
+ if (bytes_read == 0) {
+ if (!feof(f)) {
+ if (progress) {
+ printf("\n");
+ }
+ printf("Error: Failed to read from local file\n");
+ succeed = 0;
+ }
+ break;
+ }
+ uint32_t chunk = 0;
+ while (chunk < bytes_read) {
+ uint32_t bytes_written = 0;
+ err = afc_file_write(afc, fh, buf + chunk, bytes_read - chunk, &bytes_written);
+ if (err != AFC_E_SUCCESS) {
+ if (progress) {
+ printf("\n");
+ }
+ printf("Error: Failed to write to device file\n");
+ succeed = 0;
+ break;
+ }
+ chunk += bytes_written;
+ }
+ total += chunk;
+ if (progress) {
+ int prog = (int) ((double) total / (double) fst.st_size * 100.0f);
+ if (prog > lastprog) {
+ gettimeofday(&t2, NULL);
+ timeval_subtract(&tdiff, &t2, &t1);
+ double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
+ printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
+ fflush(stdout);
+ lastprog = prog;
+ }
+ }
+ }
+ free(buf);
+ afc_file_close(afc, fh);
+ fclose(f);
+ return succeed;
+}
+
+static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_put)
+{
+ if (is_directory(srcpath)) {
+ if (!recursive_put) {
+ printf("Error: Failed to put directory without '-r' option: %s\n", srcpath);
+ return 0;
+ }
+ plist_t info = NULL;
+ afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info);
+ //create if target directory does not exist
+ plist_free(info);
+ info = NULL;
+ if (err == AFC_E_OBJECT_NOT_FOUND) {
+ err = afc_make_directory(afc, dstpath);
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to create directory '%s': %s (%d)\n", dstpath, afc_strerror(err), err);
+ return 0;
+ }
+ } else if (!force_overwrite) {
+ printf("Error: Failed to put existing directory without '-f' option: %s\n", dstpath);
+ return 0;
+ }
+ afc_get_file_info_plist(afc, dstpath, &info);
+ uint8_t is_dir = 0;
+ if (info) {
+ const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
+ plist_free(info);
+ }
+ if (!is_dir) {
+ printf("Error: Failed to create or access directory: '%s'\n", dstpath);
+ return 0;
+ }
+
+ // walk dir recursively to put files
+ DIR *cur_dir = opendir(srcpath);
+ if (cur_dir) {
+ struct dirent *ep;
+ while ((ep = readdir(cur_dir))) {
+ if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
+ continue;
+ }
+ char *fpath = string_build_path(srcpath, ep->d_name, NULL);
+ if (fpath) {
+ uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
+ size_t len = dst_is_root ? (strlen(ep->d_name) + 2) : (strlen(dstpath) + 1 + strlen(ep->d_name) + 1);
+ char *newdst = (char *) malloc(len);
+ if (dst_is_root) {
+ snprintf(newdst, len, "/%s", ep->d_name);
+ } else {
+ snprintf(newdst, len, "%s/%s", dstpath, ep->d_name);
+ }
+ if (!put_file(afc, fpath, newdst, force_overwrite, recursive_put)) {
+ free(newdst);
+ free(fpath);
+ return 0;
+ }
+ free(newdst);
+ free(fpath);
+ }
+ }
+ closedir(cur_dir);
+ } else {
+ printf("Error: Failed to visit directory: '%s': %s\n", srcpath, strerror(errno));
+ return 0;
+ }
+ } else {
+ return put_single_file(afc, srcpath, dstpath, force_overwrite);
+ }
+ return 1;
+}
+
+static void handle_put(afc_client_t afc, int argc, char **argv)
+{
+ if (argc < 1) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+ int i = 0;
+ uint8_t force_overwrite = 0, recursive_put = 0;
+ for ( ; i < argc; i++) {
+ if (!strcmp(argv[i], "--")) {
+ i++;
+ break;
+ } else if (!strcmp(argv[i], "-r")) {
+ recursive_put = 1;
+ } else if (!strcmp(argv[i], "-f")) {
+ force_overwrite = 1;
+ } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
+ recursive_put = 1;
+ force_overwrite = 1;
+ } else {
+ break;
+ }
+ }
+ if (i >= argc) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+ char *srcpath = strdup(argv[i]);
+ size_t src_len = strlen(srcpath);
+ if (src_len > 1 && srcpath[src_len - 1] == '/') {
+ srcpath[src_len - 1] = '\0';
+ }
+ char *dstpath = NULL;
+ if (argc - i == 1) {
+ dstpath = get_absolute_path(path_get_basename(srcpath));
+ } else if (argc - i == 2) {
+ char *tmp = strdup(argv[i + 1]);
+ size_t dst_len = strlen(tmp);
+ if (dst_len > 1 && tmp[dst_len - 1] == '/') {
+ tmp[dst_len - 1] = '\0';
+ }
+ dstpath = get_absolute_path(tmp);
+ free(tmp);
+ } else {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+ plist_t info = NULL;
+ afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info);
+ // target does not exist, put directly
+ if (err == AFC_E_OBJECT_NOT_FOUND) {
+ put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
+ free(srcpath);
+ free(dstpath);
+ } else {
+ uint8_t is_dir = 0;
+ if (info) {
+ const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
+ plist_free(info);
+ }
+ // target is a directory, try to put under this directory
+ if (is_dir) {
+ const char *basen = path_get_basename(srcpath);
+ uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
+ size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1);
+ char *newdst = (char *) malloc(len);
+ if (dst_is_root) {
+ snprintf(newdst, len, "/%s", basen);
+ } else {
+ snprintf(newdst, len, "%s/%s", dstpath, basen);
+ }
+ free(dstpath);
+ dstpath = get_absolute_path(newdst);
+ free(newdst);
+ put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
+ } else {
+ //target is common file, rewrite it
+ put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
+ }
+ free(srcpath);
+ free(dstpath);
+ }
+}
+
+static void handle_pwd(afc_client_t afc, int argc, char** argv)
+{
+ printf("%s\n", curdir);
+}
+
+static void handle_cd(afc_client_t afc, int argc, char** argv)
+{
+ if (argc != 1) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+
+ if (!strcmp(argv[0], ".")) {
+ return;
+ }
+
+ if (!strcmp(argv[0], "..")) {
+ if (!strcmp(curdir, "/")) {
+ return;
+ }
+ char *p = strrchr(curdir, '/');
+ if (!p) {
+ strcpy(curdir, "/");
+ return;
+ }
+ if (p == curdir) {
+ *(p+1) = '\0';
+ } else {
+ *p = '\0';
+ }
+ return;
+ }
+
+ char* path = get_realpath(argv[0]);
+ int is_dir = 0;
+ plist_t info = NULL;
+ afc_error_t err = afc_get_file_info_plist(afc, path, &info);
+ if (err == AFC_E_SUCCESS && info) {
+ const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
+ plist_free(info);
+ } else {
+ printf("Error: Failed to get file info for %s: %s (%d)\n", path, afc_strerror(err), err);
+ free(path);
+ return;
+ }
+
+ if (!is_dir) {
+ printf("Error: '%s' is not a valid directory\n", path);
+ free(path);
+ return;
+ }
+
+ free(curdir);
+ curdir = path;
+ curdir_len = strlen(curdir);
+}
+
+static void parse_cmdline(int* p_argc, char*** p_argv, const char* cmdline)
+{
+ char **argv = NULL;
+ int argc = 0;
+ size_t maxlen = strlen(cmdline);
+ const char* pos = cmdline;
+ const char* qpos = NULL;
+ char *tmpbuf = NULL;
+ int tmplen = 0;
+ int is_error = 0;
+
+ /* skip initial whitespace */
+ while (isspace(*pos)) pos++;
+ maxlen -= (pos - cmdline);
+
+ tmpbuf = (char*)malloc(maxlen+1);
+
+ while (!is_error) {
+ if (*pos == '\\') {
+ pos++;
+ switch (*pos) {
+ case '"':
+ case '\'':
+ case '\\':
+ case ' ':
+ tmpbuf[tmplen++] = *pos;
+ pos++;
+ break;
+ default:
+ printf("Error: Invalid escape sequence\n");
+ is_error++;
+ break;
+ }
+ } else if (*pos == '"' || *pos == '\'') {
+ if (!qpos) {
+ qpos = pos;
+ } else {
+ qpos = NULL;
+ }
+ pos++;
+ } else if (*pos == '\0' || (!qpos && isspace(*pos))) {
+ tmpbuf[tmplen] = '\0';
+ if (*pos == '\0' && qpos) {
+ printf("Error: Unmatched `%c`\n", *qpos);
+ is_error++;
+ break;
+ }
+ char** new_argv = (char**)realloc(argv, (argc+1)*sizeof(char*));
+ if (new_argv == NULL) {
+ printf("Error: Out of memory?!\n");
+ is_error++;
+ break;
+ }
+ argv = new_argv;
+ /* shrink buffer to actual argument size */
+ argv[argc] = (char*)realloc(tmpbuf, tmplen+1);
+ if (!argv[argc]) {
+ printf("Error: Out of memory?!\n");
+ is_error++;
+ break;
+ }
+ argc++;
+ tmpbuf = NULL;
+ if (*pos == '\0') {
+ break;
+ }
+ maxlen -= tmplen;
+ tmpbuf = (char*)malloc(maxlen+1);
+ tmplen = 0;
+ while (isspace(*pos)) pos++;
+ } else {
+ tmpbuf[tmplen++] = *pos;
+ pos++;
+ }
+ }
+ if (tmpbuf) {
+ free(tmpbuf);
+ }
+ if (is_error) {
+ int i;
+ for (i = 0; argv && i < argc; i++) free(argv[i]);
+ free(argv);
+ return;
+ }
+
+ *p_argv = argv;
+ *p_argc = argc;
+}
+
+static int process_args(afc_client_t afc, int argc, char** argv)
+{
+ if (!strcmp(argv[0], "q") || !strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
+ return -1;
+ }
+ else if (!strcmp(argv[0], "help")) {
+ handle_help(afc, argc, argv);
+ }
+ else if (!strcmp(argv[0], "devinfo") || !strcmp(argv[0], "deviceinfo")) {
+ handle_devinfo(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "info")) {
+ handle_file_info(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) {
+ handle_list(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) {
+ handle_rename(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "mkdir")) {
+ handle_mkdir(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "ln")) {
+ handle_link(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove")) {
+ handle_remove(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "get")) {
+ handle_get(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "put")) {
+ handle_put(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "pwd")) {
+ handle_pwd(afc, argc-1, argv+1);
+ }
+ else if (!strcmp(argv[0], "cd")) {
+ handle_cd(afc, argc-1, argv+1);
+ }
+ else {
+ printf("Unknown command '%s'. Type 'help' to get a list of available commands.\n", argv[0]);
+ }
+ return 0;
+}
+
+static void start_cmdline(afc_client_t afc)
+{
+ while (!stop_requested) {
+ int argc = 0;
+ char **argv = NULL;
+ char prompt[128];
+ int plen = curdir_len;
+ char *ppath = curdir;
+ int plim = (int)(sizeof(prompt)/2)-8;
+ if (plen > plim) {
+ ppath = curdir + (plen - plim);
+ plen = plim;
+ }
+ snprintf(prompt, 128, FG_BLACK BG_LIGHT_GRAY "afc:" COLOR_RESET FG_BRIGHT_YELLOW BG_BLUE "%.*s" COLOR_RESET " > ", plen, ppath);
+#ifdef HAVE_READLINE
+ char* cmd = readline(prompt);
+ if (!cmd || !*cmd) {
+ free(cmd);
+ continue;
+ }
+ add_history(cmd);
+ parse_cmdline(&argc, &argv, cmd);
+#else
+ char cmdbuf[4096];
+ printf("%s", prompt);
+ fflush(stdout);
+ get_input(cmdbuf, sizeof(cmdbuf));
+ parse_cmdline(&argc, &argv, cmdbuf);
+#endif
+#ifdef HAVE_READLINE
+ free(cmd);
+#endif
+ /* process arguments */
+ if (argv && argv[0]) {
+ if (process_args(afc, argc, argv) < 0) {
+ break;
+ }
+ }
+ }
+}
+
+static void device_event_cb(const idevice_event_t* event, void* userdata)
+{
+ if (use_network && event->conn_type != CONNECTION_NETWORK) {
+ return;
+ } else if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
+ return;
+ }
+ if (event->event == IDEVICE_DEVICE_ADD) {
+ if (!udid) {
+ udid = strdup(event->udid);
+ }
+ if (strcmp(udid, event->udid) == 0) {
+ connected = 1;
+ }
+ } else if (event->event == IDEVICE_DEVICE_REMOVE) {
+ if (strcmp(udid, event->udid) == 0) {
+ connected = 0;
+ printf("\n[disconnected]\n");
+ handle_signal(SIGINT);
+ }
+ }
+}
+
+int main(int argc, char** argv)
+{
+ const char* appid = NULL;
+ int ret = 0;
+ idevice_t device = NULL;
+ lockdownd_client_t lockdown = NULL;
+ lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
+ lockdownd_service_descriptor_t service = NULL;
+ afc_client_t afc = NULL;
+ house_arrest_client_t house_arrest = NULL;
+ const char* service_name = AFC_SERVICE_NAME;
+ int use_container = 0;
+
+ int c = 0;
+ const struct option longopts[] = {
+ { "udid", required_argument, NULL, 'u' },
+ { "network", no_argument, NULL, 'n' },
+ { "help", no_argument, NULL, 'h' },
+ { "debug", no_argument, NULL, 'd' },
+ { "version", no_argument, NULL, 'v' },
+ { "documents", required_argument, NULL, OPT_DOCUMENTS },
+ { "container", required_argument, NULL, OPT_CONTAINER },
+ { NULL, 0, NULL, 0}
+ };
+
+ signal(SIGTERM, handle_signal);
+#ifndef _WIN32
+ signal(SIGQUIT, handle_signal);
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ while ((c = getopt_long(argc, argv, "du:nhv", 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;
+ }
+ udid = strdup(optarg);
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'h':
+ print_usage(argc, argv, 0);
+ return 0;
+ case 'v':
+ printf("%s %s", TOOL_NAME, PACKAGE_VERSION);
+#ifdef HAVE_READLINE
+ printf(" (readline)");
+#endif
+ printf("\n");
+ return 0;
+ case OPT_DOCUMENTS:
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: '--documents' requires a non-empty app ID!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ appid = optarg;
+ use_container = 0;
+ break;
+ case OPT_CONTAINER:
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: '--container' requires a not-empty app ID!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ appid = optarg;
+ use_container = 1;
+ break;
+ default:
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ 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) {
+ fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n");
+ return 1;
+ }
+
+ idevice_events_subscribe(&context, device_event_cb, NULL);
+
+ while (!connected && !stop_requested) {
+#ifdef _WIN32
+ Sleep(100);
+#else
+ usleep(100000);
+#endif
+ }
+ if (stop_requested) {
+ return 0;
+ }
+
+ ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
+ if (ret != IDEVICE_E_SUCCESS) {
+ if (udid) {
+ fprintf(stderr, "ERROR: Device %s not found!\n", udid);
+ } else {
+ fprintf(stderr, "ERROR: No device found!\n");
+ }
+ return 1;
+ }
+
+ do {
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
+ fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
+ ret = 1;
+ break;
+ }
+
+ if (appid) {
+ service_name = HOUSE_ARREST_SERVICE_NAME;
+ }
+
+ ldret = lockdownd_start_service(lockdown, service_name, &service);
+ if (ldret != LOCKDOWN_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to start service %s: %s (%d)\n", service_name, lockdownd_strerror(ldret), ldret);
+ ret = 1;
+ break;
+ }
+
+ if (appid) {
+ house_arrest_client_new(device, service, &house_arrest);
+ if (!house_arrest) {
+ fprintf(stderr, "Could not start document sharing service!\n");
+ ret = 1;
+ break;
+ }
+
+ if (house_arrest_send_command(house_arrest, use_container ? "VendContainer": "VendDocuments", appid) != HOUSE_ARREST_E_SUCCESS) {
+ fprintf(stderr, "Could not send house_arrest command!\n");
+ ret = 1;
+ break;
+ }
+
+ plist_t dict = NULL;
+ if (house_arrest_get_result(house_arrest, &dict) != HOUSE_ARREST_E_SUCCESS) {
+ fprintf(stderr, "Could not get result from document sharing service!\n");
+ break;
+ }
+ plist_t node = plist_dict_get_item(dict, "Error");
+ if (node) {
+ char *str = NULL;
+ plist_get_string_val(node, &str);
+ fprintf(stderr, "ERROR: %s\n", str);
+ if (str && !strcmp(str, "InstallationLookupFailed")) {
+ fprintf(stderr, "The App '%s' is either not present on the device, or the 'UIFileSharingEnabled' key is not set in its Info.plist. Starting with iOS 8.3 this key is mandatory to allow access to an app's Documents folder.\n", appid);
+ }
+ free(str);
+ plist_free(dict);
+ break;
+ }
+ plist_free(dict);
+ afc_client_new_from_house_arrest_client(house_arrest, &afc);
+ } else {
+ afc_client_new(device, service, &afc);
+ }
+ lockdownd_service_descriptor_free(service);
+ lockdownd_client_free(lockdown);
+ lockdown = NULL;
+
+ curdir = strdup("/");
+ curdir_len = 1;
+
+ if (argc > 0) {
+ // command line mode
+ process_args(afc, argc, argv);
+ } else {
+ // interactive mode
+ start_cmdline(afc);
+ }
+
+ } while (0);
+
+ if (afc) {
+ afc_client_free(afc);
+ }
+ if (lockdown) {
+ lockdownd_client_free(lockdown);
+ }
+ idevice_free(device);
+
+ return ret;
+}
diff --git a/tools/idevicebackup.c b/tools/idevicebackup.c
index 2856fda..363abad 100644
--- a/tools/idevicebackup.c
+++ b/tools/idevicebackup.c
@@ -32,31 +32,19 @@
#include <stdlib.h>
#include <signal.h>
#include <getopt.h>
-#if defined(HAVE_OPENSSL)
-#include <openssl/sha.h>
-#elif defined(HAVE_GNUTLS)
-#include <gcrypt.h>
-#elif defined(HAVE_MBEDTLS)
-#include <mbedtls/sha1.h>
-#if MBEDTLS_VERSION_NUMBER < 0x03000000
-#define mbedtls_sha1 mbedtls_sha1_ret
-#define mbedtls_sha1_starts mbedtls_sha1_starts_ret
-#define mbedtls_sha1_update mbedtls_sha1_update_ret
-#define mbedtls_sha1_finish mbedtls_sha1_finish_ret
-#endif
-#else
-#error No supported crypto library enabled
-#endif
#include <unistd.h>
#include <ctype.h>
#include <time.h>
+#include <sys/stat.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/mobilebackup.h>
#include <libimobiledevice/notification_proxy.h>
#include <libimobiledevice/afc.h>
+#include <libimobiledevice-glue/sha.h>
#include <libimobiledevice-glue/utils.h>
+#include <plist/plist.h>
#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup"
#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
@@ -64,7 +52,7 @@
#define LOCK_ATTEMPTS 50
#define LOCK_WAIT 200000
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#endif
@@ -87,17 +75,6 @@ enum device_link_file_status_t {
DEVICE_LINK_FILE_STATUS_LAST_HUNK
};
-static void sha1_of_data(const char *input, uint32_t size, unsigned char *hash_out)
-{
-#if defined(HAVE_OPENSSL)
- SHA1((const unsigned char*)input, size, hash_out);
-#elif defined(HAVE_GNUTLS)
- gcry_md_hash_buffer(GCRY_MD_SHA1, hash_out, input, size);
-#elif defined(HAVE_MBEDTLS)
- mbedtls_sha1((unsigned char*)input, size, hash_out);
-#endif
-}
-
static int compare_hash(const unsigned char *hash1, const unsigned char *hash2, int hash_len)
{
int i;
@@ -109,89 +86,49 @@ static int compare_hash(const unsigned char *hash1, const unsigned char *hash2,
return 1;
}
-static void _sha1_update(void* context, const char* data, size_t len)
-{
-#if defined(HAVE_OPENSSL)
- SHA1_Update(context, data, len);
-#elif defined(HAVE_GNUTLS)
- gcry_md_write(context, data, len);
-#elif defined(HAVE_MBEDTLS)
- mbedtls_sha1_update(context, (const unsigned char*)data, len);
-#endif
-}
-
static void compute_datahash(const char *path, const char *destpath, uint8_t greylist, const char *domain, const char *appid, const char *version, unsigned char *hash_out)
{
-#if defined(HAVE_OPENSSL)
- SHA_CTX sha1;
- SHA1_Init(&sha1);
- void* psha1 = &sha1;
-#elif defined(HAVE_GNUTLS)
- gcry_md_hd_t hd = NULL;
- gcry_md_open(&hd, GCRY_MD_SHA1, 0);
- if (!hd) {
- printf("ERROR: Could not initialize libgcrypt/SHA1\n");
- return;
- }
- gcry_md_reset(hd);
- void* psha1 = hd;
-#elif defined(HAVE_MBEDTLS)
- mbedtls_sha1_context sha1;
- mbedtls_sha1_init(&sha1);
- mbedtls_sha1_starts(&sha1);
- void* psha1 = &sha1;
-#endif
+ sha1_context sha1;
+ sha1_init(&sha1);
FILE *f = fopen(path, "rb");
if (f) {
unsigned char buf[16384];
size_t len;
while ((len = fread(buf, 1, 16384, f)) > 0) {
- _sha1_update(psha1, (const char*)buf, len);
+ sha1_update(&sha1, buf, len);
}
fclose(f);
- _sha1_update(psha1, destpath, strlen(destpath));
- _sha1_update(psha1, ";", 1);
+ sha1_update(&sha1, destpath, strlen(destpath));
+ sha1_update(&sha1, ";", 1);
if (greylist == 1) {
- _sha1_update(psha1, "true", 4);
+ sha1_update(&sha1, "true", 4);
} else {
- _sha1_update(psha1, "false", 5);
+ sha1_update(&sha1, "false", 5);
}
- _sha1_update(psha1, ";", 1);
+ sha1_update(&sha1, ";", 1);
if (domain) {
- _sha1_update(psha1, domain, strlen(domain));
+ sha1_update(&sha1, domain, strlen(domain));
} else {
- _sha1_update(psha1, "(null)", 6);
+ sha1_update(&sha1, "(null)", 6);
}
- _sha1_update(psha1, ";", 1);
+ sha1_update(&sha1, ";", 1);
if (appid) {
- _sha1_update(psha1, appid, strlen(appid));
+ sha1_update(&sha1, appid, strlen(appid));
} else {
- _sha1_update(psha1, "(null)", 6);
+ sha1_update(&sha1, "(null)", 6);
}
- _sha1_update(psha1, ";", 1);
+ sha1_update(&sha1, ";", 1);
if (version) {
- _sha1_update(psha1, version, strlen(version));
+ sha1_update(&sha1, version, strlen(version));
} else {
- _sha1_update(psha1, "(null)", 6);
+ sha1_update(&sha1, "(null)", 6);
}
-#if defined(HAVE_OPENSSL)
- SHA1_Final(hash_out, &sha1);
-#elif defined(HAVE_GNUTLS)
- unsigned char *newhash = gcry_md_read(hd, GCRY_MD_SHA1);
- memcpy(hash_out, newhash, 20);
-#elif defined(HAVE_MBEDTLS)
- mbedtls_sha1_finish(&sha1, hash_out);
-#endif
+ sha1_final(&sha1, hash_out);
}
-#if defined(HAVE_GNUTLS)
- gcry_md_close(hd);
-#elif defined(HAVE_MBEDTLS)
- mbedtls_sha1_free(&sha1);
-#endif
}
static void print_hash(const unsigned char *hash, int len)
@@ -239,7 +176,13 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid)
if (value_node)
plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
- plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
+ plist_dict_set_item(ret, "Last Backup Date",
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_new_unix_date(time(NULL))
+#else
+ plist_new_date(time(NULL) - MAC_EPOCH, 0)
+#endif
+ );
value_node = plist_dict_get_item(root_node, "ProductType");
plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
@@ -276,7 +219,11 @@ static void mobilebackup_info_update_last_backup_date(plist_t info_plist)
return;
node = plist_dict_get_item(info_plist, "Last Backup Date");
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_set_unix_date_val(node, time(NULL));
+#else
plist_set_date_val(node, time(NULL) - MAC_EPOCH, 0);
+#endif
node = NULL;
}
@@ -317,7 +264,7 @@ static void mobilebackup_write_status(const char *path, int status)
if (stat(file_path, &st) == 0)
remove(file_path);
- plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML);
+ plist_write_to_file(status_plist, file_path, PLIST_FORMAT_XML, 0);
plist_free(status_plist);
status_plist = NULL;
@@ -331,7 +278,7 @@ static int mobilebackup_read_status(const char *path)
plist_t status_plist = NULL;
char *file_path = mobilebackup_build_path(path, "Status", ".plist");
- plist_read_from_filename(&status_plist, file_path);
+ plist_read_from_file(file_path, &status_plist, NULL);
free(file_path);
if (!status_plist) {
printf("Could not read Status.plist!\n");
@@ -454,7 +401,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
}
infopath = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
- plist_read_from_filename(&mdinfo, infopath);
+ plist_read_from_file(infopath, &mdinfo, NULL);
free(infopath);
if (!mdinfo) {
printf("\r\n");
@@ -528,7 +475,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
unsigned char fnhash[20];
char fnamehash[41];
char *p = fnamehash;
- sha1_of_data(fnstr, strlen(fnstr), fnhash);
+ sha1((const unsigned char*)fnstr, strlen(fnstr), fnhash);
free(fnstr);
int i;
for ( i = 0; i < 20; i++, p += 2 ) {
@@ -706,7 +653,7 @@ int main(int argc, char *argv[])
/* we need to exit cleanly on running backups and restores or we cause havok */
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
@@ -882,7 +829,7 @@ int main(int argc, char *argv[])
/* verify existing Info.plist */
if (stat(info_path, &st) == 0) {
printf("Reading Info.plist from backup.\n");
- plist_read_from_filename(&info_plist, info_path);
+ plist_read_from_file(info_path, &info_plist, NULL);
if (!info_plist) {
printf("Could not read Info.plist\n");
@@ -893,7 +840,7 @@ int main(int argc, char *argv[])
/* update the last backup time within Info.plist */
mobilebackup_info_update_last_backup_date(info_plist);
remove(info_path);
- plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
+ plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
} else {
printf("Aborting backup. Backup is not compatible with the current device.\n");
cmd = CMD_LEAVE;
@@ -959,7 +906,7 @@ int main(int argc, char *argv[])
/* read the last Manifest.plist */
if (!is_full_backup) {
printf("Reading existing Manifest.\n");
- plist_read_from_filename(&manifest_plist, manifest_path);
+ plist_read_from_file(manifest_path, &manifest_plist, NULL);
if (!manifest_plist) {
printf("Could not read Manifest.plist, switching to full backup mode.\n");
is_full_backup = 1;
@@ -977,7 +924,7 @@ int main(int argc, char *argv[])
remove(info_path);
printf("Creating Info.plist for new backup.\n");
info_plist = mobilebackup_factory_info_plist_new(udid);
- plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
+ plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
}
free(info_path);
@@ -1116,7 +1063,7 @@ int main(int argc, char *argv[])
remove(filename_mdinfo);
node = plist_dict_get_item(node_tmp, "BackupFileInfo");
- plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY);
+ plist_write_to_file(node, filename_mdinfo, PLIST_FORMAT_BINARY, 0);
free(filename_mdinfo);
}
@@ -1228,7 +1175,7 @@ files_out:
if (manifest_plist) {
remove(manifest_path);
printf("Storing Manifest.plist...\n");
- plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML);
+ plist_write_to_file(manifest_plist, manifest_path, PLIST_FORMAT_XML, 0);
}
backup_ok = 1;
@@ -1259,21 +1206,21 @@ files_out:
}
/* now make sure backup integrity is ok! verify all files */
printf("Reading existing Manifest.\n");
- plist_read_from_filename(&manifest_plist, manifest_path);
+ plist_read_from_file(manifest_path, &manifest_plist, NULL);
if (!manifest_plist) {
printf("Could not read Manifest.plist. Aborting.\n");
break;
}
printf("Verifying backup integrity, please wait.\n");
- char *bin = NULL;
+ unsigned char *bin = NULL;
uint64_t binsize = 0;
node = plist_dict_get_item(manifest_plist, "Data");
if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
printf("Could not read Data key from Manifest.plist!\n");
break;
}
- plist_get_data_val(node, &bin, &binsize);
+ plist_get_data_val(node, (char**)&bin, &binsize);
plist_t backup_data = NULL;
if (bin) {
char *auth_ver = NULL;
@@ -1290,7 +1237,7 @@ files_out:
if (auth_sig && (auth_sig_len == 20)) {
/* calculate the sha1, then compare */
unsigned char data_sha1[20];
- sha1_of_data(bin, binsize, data_sha1);
+ sha1(bin, binsize, data_sha1);
if (compare_hash(auth_sig, data_sha1, 20)) {
printf("AuthSignature is valid\n");
} else {
@@ -1303,7 +1250,7 @@ files_out:
} else if (auth_ver) {
printf("Unknown AuthVersion '%s', cannot verify AuthSignature\n", auth_ver);
}
- plist_from_bin(bin, (uint32_t)binsize, &backup_data);
+ plist_from_bin((char*)bin, (uint32_t)binsize, &backup_data);
free(bin);
}
if (!backup_data) {
@@ -1386,7 +1333,7 @@ files_out:
while (node) {
/* TODO: read mddata/mdinfo files and send to device using DLSendFile */
file_info_path = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
- plist_read_from_filename(&file_info, file_info_path);
+ plist_read_from_file(file_info_path, &file_info, NULL);
/* get encryption state */
tmp_node = plist_dict_get_item(file_info, "IsEncrypted");
@@ -1416,7 +1363,7 @@ files_out:
file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata");
/* determine file size */
-#ifdef WIN32
+#ifdef _WIN32
struct _stati64 fst;
if (_stati64(file_info_path, &fst) != 0)
#else
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
index 8e53aaf..12d6083 100644
--- a/tools/idevicebackup2.c
+++ b/tools/idevicebackup2.c
@@ -47,13 +47,14 @@
#include <libimobiledevice/sbservices.h>
#include <libimobiledevice/diagnostics_relay.h>
#include <libimobiledevice-glue/utils.h>
+#include <plist/plist.h>
#include <endianness.h>
#define LOCK_ATTEMPTS 50
#define LOCK_WAIT 200000
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#define sleep(x) Sleep(x*1000)
@@ -73,6 +74,7 @@
static int verbose = 1;
static int quit_flag = 0;
+static int passcode_requested = 0;
#define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
@@ -114,6 +116,10 @@ static void notify_cb(const char *notification, void *userdata)
quit_flag++;
} else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
backup_domain_changed = 1;
+ } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.presented")) {
+ passcode_requested = 1;
+ } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.dismissed")) {
+ passcode_requested = 0;
} else {
PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
}
@@ -125,21 +131,15 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil
return;
}
- char **fileinfo = NULL;
+ plist_t fileinfo = NULL;
uint32_t fsize = 0;
- afc_get_file_info(afc, filename, &fileinfo);
+ afc_get_file_info_plist(afc, filename, &fileinfo);
if (!fileinfo) {
return;
}
- int i;
- for (i = 0; fileinfo[i]; i+=2) {
- if (!strcmp(fileinfo[i], "st_size")) {
- fsize = atol(fileinfo[i+1]);
- break;
- }
- }
- afc_dictionary_free(fileinfo);
+ fsize = plist_dict_get_uint(fileinfo, "st_size");
+ plist_free(fileinfo);
if (fsize == 0) {
return;
@@ -172,7 +172,7 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil
static int __mkdir(const char* path, int mode)
{
-#ifdef WIN32
+#ifdef _WIN32
return mkdir(path);
#else
return mkdir(path, mode);
@@ -201,7 +201,7 @@ static int mkdir_with_parents(const char *dir, int mode)
return res;
}
-#ifdef WIN32
+#ifdef _WIN32
static int win32err_to_errno(int err_value)
{
switch (err_value) {
@@ -218,7 +218,7 @@ static int win32err_to_errno(int err_value)
static int remove_file(const char* path)
{
int e = 0;
-#ifdef WIN32
+#ifdef _WIN32
if (!DeleteFile(path)) {
e = win32err_to_errno(GetLastError());
}
@@ -233,7 +233,7 @@ static int remove_file(const char* path)
static int remove_directory(const char* path)
{
int e = 0;
-#ifdef WIN32
+#ifdef _WIN32
if (!RemoveDirectory(path)) {
e = win32err_to_errno(GetLastError());
}
@@ -454,7 +454,13 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
/* Installed Applications */
plist_dict_set_item(ret, "Installed Applications", installed_apps);
- plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
+ plist_dict_set_item(ret, "Last Backup Date",
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_new_unix_date(time(NULL))
+#else
+ plist_new_date(time(NULL) - MAC_EPOCH, 0)
+#endif
+ );
value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");
if (value_node)
@@ -606,7 +612,7 @@ static int mb2_status_check_snapshot_state(const char *path, const char *udid, c
plist_t status_plist = NULL;
char *file_path = string_build_path(path, udid, "Status.plist", NULL);
- plist_read_from_filename(&status_plist, file_path);
+ plist_read_from_file(file_path, &status_plist, NULL);
free(file_path);
if (!status_plist) {
printf("Could not read Status.plist!\n");
@@ -767,7 +773,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
uint32_t bytes = 0;
char *localfile = string_build_path(backup_dir, path, NULL);
char buf[32768];
-#ifdef WIN32
+#ifdef _WIN32
struct _stati64 fst;
#else
struct stat fst;
@@ -778,7 +784,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
int errcode = -1;
int result = -1;
uint32_t length;
-#ifdef WIN32
+#ifdef _WIN32
uint64_t total;
uint64_t sent;
#else
@@ -809,7 +815,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
goto leave_proto_err;
}
-#ifdef WIN32
+#ifdef _WIN32
if (_stati64(localfile, &fst) < 0)
#else
if (stat(localfile, &fst) < 0)
@@ -1217,7 +1223,12 @@ static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plis
plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));
plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
plist_dict_set_item(fdict, "DLFileModificationDate",
- plist_new_date(st.st_mtime - MAC_EPOCH, 0));
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_new_unix_date(st.st_mtime)
+#else
+ plist_new_date(st.st_mtime - MAC_EPOCH, 0)
+#endif
+ );
plist_dict_set_item(dirlist, ep->d_name, fdict);
free(fpath);
@@ -1342,7 +1353,7 @@ static void mb2_copy_directory_by_path(const char *src, const char *dst)
}
}
-#ifdef WIN32
+#ifdef _WIN32
#define BS_CC '\b'
#define my_getch getch
#else
@@ -1462,8 +1473,6 @@ static void print_usage(int argc, char **argv, int is_error)
);
}
-#define DEVICE_VERSION(maj, min, patch) ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF))
-
int main(int argc, char *argv[])
{
idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
@@ -1521,7 +1530,7 @@ int main(int argc, char *argv[])
{ "settings", no_argument, NULL, OPT_SETTINGS },
{ "remove", no_argument, NULL, OPT_REMOVE },
{ "skip-apps", no_argument, NULL, OPT_SKIP_APPS },
- { "password", no_argument, NULL, OPT_PASSWORD },
+ { "password", required_argument, NULL, OPT_PASSWORD },
{ "full", no_argument, NULL, OPT_FULL },
{ NULL, 0, NULL, 0}
};
@@ -1529,7 +1538,7 @@ int main(int argc, char *argv[])
/* we need to exit cleanly on running backups and restores or we cause havok */
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
@@ -1555,6 +1564,7 @@ int main(int argc, char *argv[])
return 2;
}
source_udid = strdup(optarg);
+ break;
case 'i':
interactive_mode = 1;
break;
@@ -1594,6 +1604,7 @@ int main(int argc, char *argv[])
break;
case OPT_FULL:
cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP;
+ break;
default:
print_usage(argc, argv, 1);
return 2;
@@ -1778,7 +1789,7 @@ int main(int argc, char *argv[])
free(info_path);
}
plist_t manifest_plist = NULL;
- plist_read_from_filename(&manifest_plist, manifest_path);
+ plist_read_from_file(manifest_path, &manifest_plist, NULL);
if (!manifest_plist) {
idevice_free(device);
free(info_path);
@@ -1841,34 +1852,20 @@ int main(int argc, char *argv[])
}
/* get ProductVersion */
- char *product_version = NULL;
- int device_version = 0;
- node_tmp = NULL;
- lockdownd_get_value(lockdown, NULL, "ProductVersion", &node_tmp);
- if (node_tmp) {
- if (plist_get_node_type(node_tmp) == PLIST_STRING) {
- plist_get_string_val(node_tmp, &product_version);
- }
- plist_free(node_tmp);
- node_tmp = NULL;
- }
- if (product_version) {
- int vers[3] = { 0, 0, 0 };
- if (sscanf(product_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
- device_version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
- }
- }
+ int device_version = idevice_get_device_version(device);
/* start notification_proxy */
ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
np_client_new(device, service, &np);
np_set_notify_callback(np, notify_cb, NULL);
- const char *noties[5] = {
+ const char *noties[7] = {
NP_SYNC_CANCEL_REQUEST,
NP_SYNC_SUSPEND_REQUEST,
NP_SYNC_RESUME_REQUEST,
NP_BACKUP_DOMAIN_CHANGED,
+ "com.apple.LocalAuthentication.ui.presented",
+ "com.apple.LocalAuthentication.ui.dismissed",
NULL
};
np_observe_notifications(np, noties);
@@ -1934,7 +1931,7 @@ int main(int argc, char *argv[])
/* verify existing Info.plist */
if (info_path && (stat(info_path, &st) == 0) && cmd != CMD_CLOUD) {
PRINT_VERBOSE(1, "Reading Info.plist from backup.\n");
- plist_read_from_filename(&info_plist, info_path);
+ plist_read_from_file(info_path, &info_plist, NULL);
if (!info_plist) {
printf("Could not read Info.plist\n");
@@ -2028,7 +2025,7 @@ checkpoint:
cmd = CMD_LEAVE;
}
remove_file(info_path);
- plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
+ plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
free(info_path);
plist_free(info_plist);
@@ -2055,6 +2052,16 @@ checkpoint:
} else {
PRINT_VERBOSE(1, "Incremental backup mode.\n");
}
+ if (device_version >= IDEVICE_DEVICE_VERSION(16,1,0)) {
+ /* let's wait 2 second to see if the device passcode is requested */
+ int retries = 20;
+ while (retries-- > 0 && !passcode_requested) {
+ usleep(100000);
+ }
+ if (passcode_requested) {
+ printf("*** Waiting for passcode to be entered on the device ***\n");
+ }
+ }
} else {
if (err == MOBILEBACKUP2_E_BAD_VERSION) {
printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
@@ -2226,7 +2233,7 @@ checkpoint:
if (newpw || backup_password) {
mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
uint8_t passcode_hint = 0;
- if (device_version >= DEVICE_VERSION(13,0,0)) {
+ if (device_version >= IDEVICE_DEVICE_VERSION(13,0,0)) {
diagnostics_relay_client_t diag = NULL;
if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
plist_t dict = NULL;
@@ -2304,7 +2311,7 @@ checkpoint:
/* device wants to know how much disk space is available on the computer */
uint64_t freespace = 0;
int res = -1;
-#ifdef WIN32
+#ifdef _WIN32
if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
res = 0;
}
@@ -2313,7 +2320,7 @@ checkpoint:
memset(&fs, '\0', sizeof(fs));
res = statvfs(backup_directory, &fs);
if (res == 0) {
- freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_bsize;
+ freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_frsize;
}
#endif
plist_t freespace_item = plist_new_uint(freespace);
diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c
index 8de6b22..ca68b59 100644
--- a/tools/idevicebtlogger.c
+++ b/tools/idevicebtlogger.c
@@ -36,7 +36,7 @@
#include <assert.h>
#include <fcntl.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#else
@@ -334,7 +334,7 @@ int main(int argc, char *argv[])
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
diff --git a/tools/idevicecrashreport.c b/tools/idevicecrashreport.c
index d0d2147..b9869ae 100644
--- a/tools/idevicecrashreport.c
+++ b/tools/idevicecrashreport.c
@@ -31,7 +31,8 @@
#include <string.h>
#include <unistd.h>
#include <getopt.h>
-#ifndef WIN32
+#include <sys/stat.h>
+#ifndef _WIN32
#include <signal.h>
#endif
#include <libimobiledevice-glue/utils.h>
@@ -42,7 +43,7 @@
#include <libimobiledevice/afc.h>
#include <plist/plist.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define S_IFLNK S_IFREG
#define S_IFSOCK S_IFREG
@@ -54,11 +55,12 @@
const char* target_directory = NULL;
static int extract_raw_crash_reports = 0;
static int keep_crash_reports = 0;
+static int remove_all = 0;
static int file_exists(const char* path)
{
struct stat tst;
-#ifdef WIN32
+#ifdef _WIN32
return (stat(path, &tst) == 0);
#else
return (lstat(path, &tst) == 0);
@@ -81,7 +83,7 @@ static int extract_raw_crash_report(const char* filename)
strcpy(p, ".crash");
/* read plist crash report */
- if (plist_read_from_filename(&report, filename)) {
+ if (plist_read_from_file(filename, &report, NULL)) {
plist_t description_node = plist_dict_get_item(report, "description");
if (description_node && plist_get_node_type(description_node) == PLIST_STRING) {
plist_get_string_val(description_node, &raw);
@@ -144,7 +146,7 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
continue;
}
- char **fileinfo = NULL;
+ plist_t fileinfo = NULL;
struct stat stbuf;
memset(&stbuf, '\0', sizeof(struct stat));
@@ -152,7 +154,7 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
strcpy(((char*)source_filename) + device_directory_length, list[k]);
/* assemble absolute target filename */
-#ifdef WIN32
+#ifdef _WIN32
/* replace every ':' with '-' since ':' is an illegal character for file names in windows */
char* current_pos = strchr(list[k], ':');
while (current_pos) {
@@ -163,7 +165,7 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
char* p = strrchr(list[k], '.');
if (p != NULL && !strncmp(p, ".synced", 7)) {
/* make sure to strip ".synced" extension as seen on iOS 5 */
- int newlen = strlen(list[k]) - 7;
+ size_t newlen = p - list[k];
strncpy(((char*)target_filename) + host_directory_length, list[k], newlen);
target_filename[host_directory_length + newlen] = '\0';
} else {
@@ -171,88 +173,93 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
}
/* get file information */
- afc_get_file_info(afc, source_filename, &fileinfo);
+ afc_get_file_info_plist(afc, source_filename, &fileinfo);
if (!fileinfo) {
printf("Failed to read information for '%s'. Skipping...\n", source_filename);
continue;
}
/* parse file information */
- int i;
- for (i = 0; fileinfo[i]; i+=2) {
- if (!strcmp(fileinfo[i], "st_size")) {
- stbuf.st_size = atoll(fileinfo[i+1]);
- } else if (!strcmp(fileinfo[i], "st_ifmt")) {
- if (!strcmp(fileinfo[i+1], "S_IFREG")) {
- stbuf.st_mode = S_IFREG;
- } else if (!strcmp(fileinfo[i+1], "S_IFDIR")) {
- stbuf.st_mode = S_IFDIR;
- } else if (!strcmp(fileinfo[i+1], "S_IFLNK")) {
- stbuf.st_mode = S_IFLNK;
- } else if (!strcmp(fileinfo[i+1], "S_IFBLK")) {
- stbuf.st_mode = S_IFBLK;
- } else if (!strcmp(fileinfo[i+1], "S_IFCHR")) {
- stbuf.st_mode = S_IFCHR;
- } else if (!strcmp(fileinfo[i+1], "S_IFIFO")) {
- stbuf.st_mode = S_IFIFO;
- } else if (!strcmp(fileinfo[i+1], "S_IFSOCK")) {
- stbuf.st_mode = S_IFSOCK;
- }
- } else if (!strcmp(fileinfo[i], "st_nlink")) {
- stbuf.st_nlink = atoi(fileinfo[i+1]);
- } else if (!strcmp(fileinfo[i], "st_mtime")) {
- stbuf.st_mtime = (time_t)(atoll(fileinfo[i+1]) / 1000000000);
- } else if (!strcmp(fileinfo[i], "LinkTarget")) {
- /* report latest crash report filename */
- printf("Link: %s\n", (char*)target_filename + strlen(target_directory));
-
- /* remove any previous symlink */
- if (file_exists(target_filename)) {
- remove(target_filename);
- }
+ stbuf.st_size = plist_dict_get_uint(fileinfo, "st_size");
+ const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(fileinfo, "st_ifmt"), NULL);
+ if (s_ifmt) {
+ if (!strcmp(s_ifmt, "S_IFREG")) {
+ stbuf.st_mode = S_IFREG;
+ } else if (!strcmp(s_ifmt, "S_IFDIR")) {
+ stbuf.st_mode = S_IFDIR;
+ } else if (!strcmp(s_ifmt, "S_IFLNK")) {
+ stbuf.st_mode = S_IFLNK;
+ } else if (!strcmp(s_ifmt, "S_IFBLK")) {
+ stbuf.st_mode = S_IFBLK;
+ } else if (!strcmp(s_ifmt, "S_IFCHR")) {
+ stbuf.st_mode = S_IFCHR;
+ } else if (!strcmp(s_ifmt, "S_IFIFO")) {
+ stbuf.st_mode = S_IFIFO;
+ } else if (!strcmp(s_ifmt, "S_IFSOCK")) {
+ stbuf.st_mode = S_IFSOCK;
+ }
+ }
+ stbuf.st_nlink = plist_dict_get_uint(fileinfo, "st_nlink");
+ stbuf.st_mtime = (time_t)(plist_dict_get_uint(fileinfo, "st_mtime") / 1000000000);
+ const char* linktarget = plist_get_string_ptr(plist_dict_get_item(fileinfo, "LinkTarget"), NULL);
+ if (linktarget && !remove_all) {
+ /* report latest crash report filename */
+ printf("Link: %s\n", (char*)target_filename + strlen(target_directory));
+
+ /* remove any previous symlink */
+ if (file_exists(target_filename)) {
+ remove(target_filename);
+ }
-#ifndef WIN32
- /* use relative filename */
- char* b = strrchr(fileinfo[i+1], '/');
- if (b == NULL) {
- b = fileinfo[i+1];
+#ifndef _WIN32
+ /* use relative filename */
+ const char* b = strrchr(linktarget, '/');
+ if (b == NULL) {
+ b = linktarget;
} else {
- b++;
- }
+ b++;
+ }
- /* create a symlink pointing to latest log */
- if (symlink(b, target_filename) < 0) {
- fprintf(stderr, "Can't create symlink to %s\n", b);
- }
+ /* create a symlink pointing to latest log */
+ if (symlink(b, target_filename) < 0) {
+ fprintf(stderr, "Can't create symlink to %s\n", b);
+ }
#endif
- if (!keep_crash_reports)
- afc_remove_path(afc, source_filename);
+ if (!keep_crash_reports)
+ afc_remove_path(afc, source_filename);
- res = 0;
- }
+ res = 0;
}
/* free file information */
- afc_dictionary_free(fileinfo);
+ plist_free(fileinfo);
/* recurse into child directories */
if (S_ISDIR(stbuf.st_mode)) {
-#ifdef WIN32
- mkdir(target_filename);
+ if (!remove_all) {
+#ifdef _WIN32
+ mkdir(target_filename);
#else
- mkdir(target_filename, 0755);
+ mkdir(target_filename, 0755);
#endif
+ }
res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename, filename_filter);
/* remove directory from device */
- if (!keep_crash_reports)
+ if (!remove_all && !keep_crash_reports)
afc_remove_path(afc, source_filename);
} else if (S_ISREG(stbuf.st_mode)) {
if (filename_filter != NULL && strstr(source_filename, filename_filter) == NULL) {
continue;
}
+ if (remove_all) {
+ printf("Remove: %s\n", source_filename);
+ afc_remove_path(afc, source_filename);
+ continue;
+ }
+
/* copy file to host */
afc_error = afc_file_open(afc, source_filename, AFC_FOPEN_RDONLY, &handle);
if(afc_error != AFC_E_SUCCESS) {
@@ -331,6 +338,7 @@ static void print_usage(int argc, char **argv, int is_error)
" -f, --filter NAME filter crash reports by NAME (case sensitive)\n"
" -h, --help prints usage information\n"
" -v, --version prints version information\n"
+ " --remove-all remove all crash logs found\n"
"\n"
"Homepage: <" PACKAGE_URL ">\n"
"Bug Reports: <" PACKAGE_BUGREPORT ">\n"
@@ -361,10 +369,11 @@ int main(int argc, char* argv[])
{ "filter", required_argument, NULL, 'f' },
{ "extract", no_argument, NULL, 'e' },
{ "keep", no_argument, NULL, 'k' },
+ { "remove-all", no_argument, NULL, 1 },
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
@@ -405,6 +414,9 @@ int main(int argc, char* argv[])
case 'k':
keep_crash_reports = 1;
break;
+ case 1:
+ remove_all = 1;
+ break;
default:
print_usage(argc, argv, 1);
return 2;
@@ -414,12 +426,16 @@ int main(int argc, char* argv[])
argv += optind;
/* ensure a target directory was supplied */
- if (!argv[0]) {
- fprintf(stderr, "ERROR: missing target directory.\n");
- print_usage(argc+optind, argv-optind, 1);
- return 2;
+ if (!remove_all) {
+ if (!argv[0]) {
+ fprintf(stderr, "ERROR: missing target directory.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+ target_directory = argv[0];
+ } else {
+ target_directory = ".";
}
- target_directory = argv[0];
/* check if target directory exists */
if (!file_exists(target_directory)) {
diff --git a/tools/idevicedate.c b/tools/idevicedate.c
index d05f63e..31b0cf7 100644
--- a/tools/idevicedate.c
+++ b/tools/idevicedate.c
@@ -33,7 +33,7 @@
#if HAVE_LANGINFO_CODESET
#include <langinfo.h>
#endif
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -43,7 +43,7 @@
#ifdef _DATE_FMT
#define DATE_FMT_LANGINFO nl_langinfo (_DATE_FMT)
#else
-#ifdef WIN32
+#ifdef _WIN32
#define DATE_FMT_LANGINFO "%a %b %#d %H:%M:%S %Z %Y"
#else
#define DATE_FMT_LANGINFO "%a %b %e %H:%M:%S %Z %Y"
@@ -104,7 +104,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/idevicedebug.c b/tools/idevicedebug.c
index fbb6c3e..3f2e289 100644
--- a/tools/idevicedebug.c
+++ b/tools/idevicedebug.c
@@ -34,7 +34,7 @@
#include <libgen.h>
#include <getopt.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#endif
@@ -239,7 +239,7 @@ int main(int argc, char *argv[])
/* map signals */
signal(SIGINT, on_signal);
signal(SIGTERM, on_signal);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, on_signal);
signal(SIGPIPE, SIG_IGN);
#endif
@@ -291,6 +291,11 @@ int main(int argc, char *argv[])
res = 0;
goto cleanup;
break;
+ default:
+ print_usage(argc, argv, 1);
+ res = 2;
+ goto cleanup;
+ break;
}
}
argc -= optind;
diff --git a/tools/idevicedebugserverproxy.c b/tools/idevicedebugserverproxy.c
index 8a3b4ff..fb082b3 100644
--- a/tools/idevicedebugserverproxy.c
+++ b/tools/idevicedebugserverproxy.c
@@ -32,7 +32,7 @@
#include <getopt.h>
#include <errno.h>
#include <signal.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#else
@@ -52,8 +52,10 @@
#define info(...) fprintf(stdout, __VA_ARGS__); fflush(stdout)
#define debug(...) if(debug_mode) fprintf(stdout, __VA_ARGS__)
+static int support_lldb = 0;
static int debug_mode = 0;
static int quit_flag = 0;
+static uint16_t local_port = 0;
typedef struct {
int client_fd;
@@ -90,6 +92,7 @@ static void print_usage(int argc, char **argv, int is_error)
" -u, --udid UDID target specific device by UDID\n"
" -n, --network connect to network device\n"
" -d, --debug enable communication debugging\n"
+ " -l, --lldb enable lldb support\n"
" -h, --help prints usage information\n"
" -v, --version prints version information\n"
"\n"
@@ -98,6 +101,26 @@ static void print_usage(int argc, char **argv, int is_error)
);
}
+static int intercept_packet(char *packet, ssize_t *packet_len) {
+ static const char kReqLaunchServer[] = "$qLaunchGDBServer;#4b";
+
+ char buffer[64] = {0};
+ if (*packet_len == (ssize_t)(sizeof(kReqLaunchServer) - 1)
+ && memcmp(packet, kReqLaunchServer, sizeof(kReqLaunchServer) - 1) == 0) {
+ sprintf(buffer, "port:%d;", local_port);
+ } else {
+ return 0;
+ }
+ int sum = 0;
+ for (size_t i = 0; i < strlen(buffer); i++) {
+ sum += buffer[i];
+ }
+ sum = sum & 255;
+ sprintf(packet, "$%s#%02x", buffer, sum);
+ *packet_len = strlen(packet);
+ return 1;
+}
+
static void* connection_handler(void* data)
{
debugserver_error_t derr = DEBUGSERVER_E_SUCCESS;
@@ -137,7 +160,10 @@ static void* connection_handler(void* data)
fprintf(stderr, "connection closed\n");
break;
}
-
+ if (support_lldb && intercept_packet(buf, &n)) {
+ socket_send(client_fd, buf, n);
+ continue;
+ }
uint32_t sent = 0;
debugserver_client_send(socket_info->debugserver_client, buf, n, &sent);
}
@@ -180,7 +206,6 @@ int main(int argc, char *argv[])
thread_info_t *thread_list = NULL;
const char* udid = NULL;
int use_network = 0;
- uint16_t local_port = 0;
int server_fd;
int result = EXIT_SUCCESS;
int c = 0;
@@ -189,11 +214,12 @@ int main(int argc, char *argv[])
{ "help", no_argument, NULL, 'h' },
{ "udid", required_argument, NULL, 'u' },
{ "network", no_argument, NULL, 'n' },
+ { "lldb", no_argument, NULL, 'l' },
{ "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
struct sigaction sa;
struct sigaction si;
memset(&sa, '\0', sizeof(struct sigaction));
@@ -234,6 +260,9 @@ int main(int argc, char *argv[])
case 'n':
use_network = 1;
break;
+ case 'l':
+ support_lldb = 1;
+ break;
case 'h':
print_usage(argc, argv, 0);
return 0;
diff --git a/tools/idevicedevmodectl.c b/tools/idevicedevmodectl.c
new file mode 100644
index 0000000..6bf1a1c
--- /dev/null
+++ b/tools/idevicedevmodectl.c
@@ -0,0 +1,462 @@
+/*
+ * idevicedevmodectl.c
+ * List or enable Developer Mode on iOS 16+ devices
+ *
+ * Copyright (c) 2022 Nikias Bassen, 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 "idevicedevmodectl"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#ifndef _WIN32
+#include <signal.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#define __usleep(x) Sleep(x/1000)
+#else
+#include <arpa/inet.h>
+#include <unistd.h>
+#define __usleep(x) usleep(x)
+#endif
+
+#include <libimobiledevice/libimobiledevice.h>
+#include <libimobiledevice/lockdown.h>
+#include <libimobiledevice/property_list_service.h>
+#include <libimobiledevice-glue/utils.h>
+
+#define AMFI_LOCKDOWN_SERVICE_NAME "com.apple.amfi.lockdown"
+
+static char* udid = NULL;
+static int use_network = 0;
+
+static void print_usage(int argc, char **argv, int is_error)
+{
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Enable Developer Mode on iOS 16+ devices or print the current status.\n"
+ "\n"
+ "Where COMMAND is one of:\n"
+ " list Print the Developer Mode status of all connected devices\n"
+ " or for a specific one if --udid is given.\n"
+ " enable Enable Developer Mode (device will reboot),\n"
+ " and confirm it after device booted up again.\n"
+ "\n"
+ " arm Arm the Developer Mode (device will reboot)\n"
+ " confirm Confirm enabling of Developer Mode\n"
+ " reveal Reveal the Developer Mode menu on the device\n"
+ "\n"
+ "The following OPTIONS are accepted:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -n, --network connect to network device\n"
+ " -d, --debug enable communication debugging\n"
+ " -h, --help print usage information\n"
+ " -v, --version print version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
+}
+
+enum {
+ OP_LIST,
+ OP_ENABLE,
+ OP_ARM,
+ OP_CONFIRM,
+ OP_REVEAL,
+ NUM_OPS
+};
+#define DEV_MODE_REVEAL 0
+#define DEV_MODE_ARM 1
+#define DEV_MODE_ENABLE 2
+
+static int get_developer_mode_status(const char* device_udid, int _use_network)
+{
+ idevice_error_t ret;
+ idevice_t device = NULL;
+ lockdownd_client_t lockdown = NULL;
+ lockdownd_error_t lerr = LOCKDOWN_E_UNKNOWN_ERROR;
+ plist_t val = NULL;
+
+ ret = idevice_new_with_options(&device, device_udid, (_use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
+ if (ret != IDEVICE_E_SUCCESS) {
+ return -1;
+ }
+
+ if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
+ idevice_free(device);
+ return -1;
+ }
+
+ lerr = lockdownd_get_value(lockdown, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Could not get DeveloperModeStatus: %s\nPlease note that this feature is only available on iOS 16+.\n", lockdownd_strerror(lerr));
+ lockdownd_client_free(lockdown);
+ idevice_free(device);
+ return -2;
+ }
+
+ uint8_t dev_mode_status = 0;
+ plist_get_bool_val(val, &dev_mode_status);
+ plist_free(val);
+
+ lockdownd_client_free(lockdown);
+ idevice_free(device);
+
+ return dev_mode_status;
+}
+
+static int amfi_service_send_msg(property_list_service_client_t amfi, plist_t msg)
+{
+ int res;
+ property_list_service_error_t perr;
+
+ perr = property_list_service_send_xml_plist(amfi, plist_copy(msg));
+ if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ fprintf(stderr, "Could not send request to device: %d\n", perr);
+ res = 2;
+ } else {
+ plist_t reply = NULL;
+ perr = property_list_service_receive_plist(amfi, &reply);
+ if (perr == PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ plist_t val = plist_dict_get_item(reply, "Error");
+ if (val) {
+ const char* err = plist_get_string_ptr(val, NULL);
+ fprintf(stderr, "Request failed: %s\n", err);
+ if (strstr(err, "passcode")) {
+ res = 2;
+ } else {
+ res = 1;
+ }
+ } else {
+ res = plist_dict_get_item(reply, "success") ? 0 : 1;
+ }
+ } else {
+ fprintf(stderr, "Could not receive reply from device: %d\n", perr);
+ res = 2;
+ }
+ plist_free(reply);
+ }
+ return res;
+}
+
+static int amfi_send_action(idevice_t device, unsigned int action)
+{
+ lockdownd_client_t lockdown = NULL;
+ lockdownd_service_descriptor_t service = NULL;
+ lockdownd_error_t lerr;
+
+ if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
+ fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lerr);
+ return 1;
+ }
+
+ lerr = lockdownd_start_service(lockdown, AMFI_LOCKDOWN_SERVICE_NAME, &service);
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ fprintf(stderr, "Could not start service %s: %s\nPlease note that this feature is only available on iOS 16+.\n", AMFI_LOCKDOWN_SERVICE_NAME, lockdownd_strerror(lerr));
+ lockdownd_client_free(lockdown);
+ return 1;
+ }
+ lockdownd_client_free(lockdown);
+ lockdown = NULL;
+
+ property_list_service_client_t amfi = NULL;
+ if (property_list_service_client_new(device, service, &amfi) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ fprintf(stderr, "Could not connect to %s on device\n", AMFI_LOCKDOWN_SERVICE_NAME);
+ if (service)
+ lockdownd_service_descriptor_free(service);
+ idevice_free(device);
+ return 1;
+ }
+ lockdownd_service_descriptor_free(service);
+
+ plist_t dict = plist_new_dict();
+ plist_dict_set_item(dict, "action", plist_new_uint(action));
+
+ int result = amfi_service_send_msg(amfi, dict);
+ plist_free(dict);
+
+ property_list_service_client_free(amfi);
+ amfi = NULL;
+
+ return result;
+}
+
+static int device_connected = 0;
+
+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 (!udid) {
+ udid = strdup(event->udid);
+ }
+ if (strcmp(udid, event->udid) == 0) {
+ device_connected = 1;
+ }
+ } else if (event->event == IDEVICE_DEVICE_REMOVE) {
+ if (strcmp(udid, event->udid) == 0) {
+ device_connected = 0;
+ }
+ }
+}
+
+
+#define WAIT_INTERVAL 200000
+#define WAIT_MAX(x) (x * (1000000 / WAIT_INTERVAL))
+#define WAIT_FOR(cond, timeout) { int __repeat = WAIT_MAX(timeout); while (!(cond) && __repeat-- > 0) { __usleep(WAIT_INTERVAL); } }
+
+int main(int argc, char *argv[])
+{
+ idevice_t device = NULL;
+ idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
+ lockdownd_client_t lockdown = NULL;
+ lockdownd_error_t lerr = LOCKDOWN_E_UNKNOWN_ERROR;
+ int res = 0;
+ int i;
+ int op = -1;
+ plist_t val = NULL;
+
+ 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' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
+ };
+
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ /* parse cmdline args */
+ while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
+ idevice_set_debug_level(1);
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ udid = optarg;
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'h':
+ print_usage(argc, argv, 0);
+ return 0;
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ return 0;
+ default:
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!argv[0]) {
+ fprintf(stderr, "ERROR: Missing command.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+
+ i = 0;
+ if (!strcmp(argv[i], "list")) {
+ op = OP_LIST;
+ }
+ else if (!strcmp(argv[i], "enable")) {
+ op = OP_ENABLE;
+ }
+ else if (!strcmp(argv[i], "arm")) {
+ op = OP_ARM;
+ }
+ else if (!strcmp(argv[i], "confirm")) {
+ op = OP_CONFIRM;
+ }
+ else if (!strcmp(argv[i], "reveal")) {
+ op = OP_REVEAL;
+ }
+
+ if ((op == -1) || (op >= NUM_OPS)) {
+ fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[i]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+
+ if (op == OP_LIST) {
+ idevice_info_t *dev_list = NULL;
+
+ if (idevice_get_device_list_extended(&dev_list, &i) < 0) {
+ fprintf(stderr, "ERROR: Unable to retrieve device list!\n");
+ return -1;
+ }
+ if (i > 0) {
+ printf("%-40s %s\n", "Device", "DeveloperMode");
+ }
+ for (i = 0; dev_list[i] != NULL; i++) {
+ if (dev_list[i]->conn_type == CONNECTION_USBMUXD && use_network) continue;
+ if (dev_list[i]->conn_type == CONNECTION_NETWORK && !use_network) continue;
+ if (udid && (strcmp(dev_list[i]->udid, udid) != 0)) continue;
+ int mode = get_developer_mode_status(dev_list[i]->udid, use_network);
+ const char *mode_str = "N/A";
+ if (mode == 1) {
+ mode_str = "enabled";
+ } else if (mode == 0) {
+ mode_str = "disabled";
+ }
+ printf("%-40s %s\n", dev_list[i]->udid, mode_str);
+ }
+ idevice_device_list_extended_free(dev_list);
+
+ return 0;
+ }
+
+ idevice_subscription_context_t context = NULL;
+ idevice_events_subscribe(&context, device_event_cb, NULL);
+
+ WAIT_FOR(device_connected, 10);
+
+ ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
+ if (ret != IDEVICE_E_SUCCESS) {
+ if (udid) {
+ printf("No device found with udid %s.\n", udid);
+ } else {
+ printf("No device found.\n");
+ }
+ return 1;
+ }
+
+ if (!udid) {
+ idevice_get_udid(device, &udid);
+ }
+
+ if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
+ fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lerr);
+ idevice_free(device);
+ return 1;
+ }
+
+ lerr = lockdownd_get_value(lockdown, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
+ lockdownd_client_free(lockdown);
+ lockdown = NULL;
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Could not get DeveloperModeStatus: %s\nPlease note that this feature is only available on iOS 16+.\n", lockdownd_strerror(lerr));
+ idevice_free(device);
+ return 1;
+ }
+
+ uint8_t dev_mode_status = 0;
+ plist_get_bool_val(val, &dev_mode_status);
+
+ if ((op == OP_ENABLE || op == OP_ARM) && dev_mode_status) {
+ if (dev_mode_status) {
+ idevice_free(device);
+ printf("DeveloperMode is already enabled.\n");
+ return 0;
+ }
+ res = 0;
+ } else {
+ if (op == OP_ENABLE || op == OP_ARM) {
+ res = amfi_send_action(device, DEV_MODE_ARM);
+ if (res == 0) {
+ if (op == OP_ARM) {
+ printf("%s: Developer Mode armed, device will reboot now.\n", udid);
+ } else {
+ printf("%s: Developer Mode armed, waiting for reboot...\n", udid);
+
+ do {
+ // waiting for device to disconnect...
+ idevice_free(device);
+ device = NULL;
+ WAIT_FOR(!device_connected, 40);
+ if (device_connected) {
+ printf("%s: ERROR: Device didn't reboot?!\n", udid);
+ res = 2;
+ break;
+ }
+ printf("disconnected\n");
+
+ // waiting for device to reconnect...
+ WAIT_FOR(device_connected, 60);
+ if (!device_connected) {
+ printf("%s: ERROR: Device didn't re-connect?!\n", udid);
+ res = 2;
+ break;
+ }
+ printf("connected\n");
+
+ idevice_new(&device, udid);
+ res = amfi_send_action(device, DEV_MODE_ENABLE);
+ } while (0);
+ if (res == 0) {
+ printf("%s: Developer Mode successfully enabled.\n", udid);
+ } else {
+ printf("%s: Failed to enable developer mode (%d)\n", udid, res);
+ }
+ }
+ } else if (res == 2) {
+ amfi_send_action(device, DEV_MODE_REVEAL);
+ printf("%s: Developer Mode could not be enabled because the device has a passcode set. You have to enable it on the device itself under Settings -> Privacy & Security -> Developer Mode.\n", udid);
+ } else {
+ printf("%s: Failed to arm Developer Mode (%d)\n", udid, res);
+ }
+ } else if (op == OP_CONFIRM) {
+ res = amfi_send_action(device, DEV_MODE_ENABLE);
+ if (res == 0) {
+ printf("%s: Developer Mode successfully enabled.\n", udid);
+ } else {
+ printf("%s: Failed to enable Developer Mode (%d)\n", udid, res);
+ }
+ } else if (op == OP_REVEAL) {
+ res = amfi_send_action(device, DEV_MODE_REVEAL);
+ if (res == 0) {
+ printf("%s: Developer Mode menu revealed successfully.\n", udid);
+ } else {
+ printf("%s: Failed to reveal Developer Mode menu (%d)\n", udid, res);
+ }
+ }
+ }
+
+ idevice_free(device);
+
+ return res;
+}
diff --git a/tools/idevicediagnostics.c b/tools/idevicediagnostics.c
index e699bc4..365c0a4 100644
--- a/tools/idevicediagnostics.c
+++ b/tools/idevicediagnostics.c
@@ -31,7 +31,7 @@
#include <getopt.h>
#include <errno.h>
#include <time.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -113,7 +113,7 @@ int main(int argc, char **argv)
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/ideviceenterrecovery.c b/tools/ideviceenterrecovery.c
index 29cc5c9..65eb882 100644
--- a/tools/ideviceenterrecovery.c
+++ b/tools/ideviceenterrecovery.c
@@ -30,7 +30,7 @@
#include <stdlib.h>
#include <getopt.h>
#include <errno.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -70,7 +70,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
index b854d5d..b319d05 100644
--- a/tools/ideviceimagemounter.c
+++ b/tools/ideviceimagemounter.c
@@ -36,7 +36,8 @@
#include <time.h>
#include <sys/time.h>
#include <inttypes.h>
-#ifndef WIN32
+#include <sys/stat.h>
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -45,8 +46,11 @@
#include <libimobiledevice/afc.h>
#include <libimobiledevice/notification_proxy.h>
#include <libimobiledevice/mobile_image_mounter.h>
-#include <asprintf.h>
+#include <libimobiledevice-glue/sha.h>
#include <libimobiledevice-glue/utils.h>
+#include <asprintf.h>
+#include <plist/plist.h>
+#include <libtatsu/tss.h>
static int list_mode = 0;
static int use_network = 0;
@@ -62,18 +66,38 @@ typedef enum {
DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
} disk_image_upload_type_t;
+enum cmd_mode {
+ CMD_NONE = 0,
+ CMD_MOUNT,
+ CMD_UNMOUNT,
+ CMD_LIST,
+ CMD_DEVMODESTATUS
+};
+
+int cmd = CMD_NONE;
+
static void print_usage(int argc, char **argv, int is_error)
{
char *name = strrchr(argv[0], '/');
- fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND [COMMAND OPTIONS...]\n", (name ? name + 1: argv[0]));
fprintf(is_error ? stderr : stdout,
"\n"
- "Mounts the specified disk image on the device.\n"
+ "Mount, list, or unmount a disk image on the device.\n"
+ "\n"
+ "COMMANDS:\n"
+ " mount PATH Mount the developer disk image at PATH.\n"
+ " For iOS 17+, PATH is a directory containing a .dmg image,\n"
+ " a BuildManifest.plist, and a Firmware sub-directory;\n"
+ " for older versions PATH is a .dmg filename with a\n"
+ " .dmg.signature in the same directory, or with another\n"
+ " parameter pointing to a file elsewhere.\n"
+ " list List mounted disk images.\n"
+ " unmount PATH Unmount the image mounted at PATH.\n"
+ " devmodestatus Query the developer mode status (iOS 16+)\n"
"\n"
"OPTIONS:\n"
" -u, --udid UDID target specific device by UDID\n"
" -n, --network connect to network device\n"
- " -l, --list List mount information\n"
" -t, --imagetype TYPE Image type to use, default is 'Developer'\n"
" -x, --xml Use XML output\n"
" -d, --debug enable communication debugging\n"
@@ -87,11 +111,11 @@ static void print_usage(int argc, char **argv, int is_error)
static void parse_opts(int argc, char **argv)
{
+ int debug_level = 0;
static struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
{ "udid", required_argument, NULL, 'u' },
{ "network", no_argument, NULL, 'n' },
- { "list", no_argument, NULL, 'l' },
{ "imagetype", required_argument, NULL, 't' },
{ "xml", no_argument, NULL, 'x' },
{ "debug", no_argument, NULL, 'd' },
@@ -101,7 +125,7 @@ static void parse_opts(int argc, char **argv)
int c;
while (1) {
- c = getopt_long(argc, argv, "hu:lt:xdnv", longopts, NULL);
+ c = getopt_long(argc, argv, "hu:t:xdnv", longopts, NULL);
if (c == -1) {
break;
}
@@ -121,9 +145,6 @@ static void parse_opts(int argc, char **argv)
case 'n':
use_network = 1;
break;
- case 'l':
- list_mode = 1;
- break;
case 't':
imagetype = optarg;
break;
@@ -131,7 +152,7 @@ static void parse_opts(int argc, char **argv)
xml_mode = 1;
break;
case 'd':
- idevice_set_debug_level(1);
+ debug_level++;
break;
case 'v':
printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
@@ -141,15 +162,8 @@ static void parse_opts(int argc, char **argv)
exit(2);
}
}
-}
-
-static void print_xml(plist_t node)
-{
- char *xml = NULL;
- uint32_t len = 0;
- plist_to_xml(node, &xml, &len);
- if (xml)
- puts(xml);
+ idevice_set_debug_level(debug_level);
+ tss_set_debug_level(debug_level);
}
static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
@@ -170,7 +184,7 @@ int main(int argc, char **argv)
size_t image_size = 0;
char *image_sig_path = NULL;
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
parse_opts(argc, argv);
@@ -178,29 +192,75 @@ int main(int argc, char **argv)
argc -= optind;
argv += optind;
- if (!list_mode) {
- if (argc < 1) {
- printf("ERROR: No IMAGE_FILE has been given!\n");
- return -1;
- }
- image_path = strdup(argv[0]);
- if (argc >= 2) {
- image_sig_path = strdup(argv[1]);
+ if (argc == 0) {
+ fprintf(stderr, "ERROR: Missing command.\n\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+
+ char* cmdstr = argv[0];
+
+ int optind2 = 0;
+ if (!strcmp(cmdstr, "mount")) {
+ cmd = CMD_MOUNT;
+ optind2++;
+ } else if (!strcmp(cmdstr, "list")) {
+ cmd = CMD_LIST;
+ optind2++;
+ } else if (!strcmp(cmdstr, "umount") || !strcmp(cmdstr, "unmount")) {
+ cmd = CMD_UNMOUNT;
+ optind2++;
+ } else if (!strcmp(cmdstr, "devmodestatus")) {
+ cmd = CMD_DEVMODESTATUS;
+ optind2++;
+ } else {
+ // assume mount command, unless -l / --list was specified
+ if (list_mode) {
+ cmd = CMD_LIST;
} else {
- if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
- printf("Out of memory?!\n");
- return -1;
- }
+ cmd = CMD_MOUNT;
}
}
+ argc -= optind2;
+ argv += optind2;
+ optind += optind2;
+
+ switch (cmd) {
+ case CMD_MOUNT:
+ if (argc < 1) {
+ fprintf(stderr, "ERROR: Missing IMAGE_FILE for mount command\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+ image_path = strdup(argv[0]);
+ if (argc >= 2) {
+ image_sig_path = strdup(argv[1]);
+ } else {
+ if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
+ printf("Out of memory?!\n");
+ return 1;
+ }
+ }
+ break;
+ case CMD_UNMOUNT:
+ if (argc != 1) {
+ fprintf(stderr, "ERROR: Missing mount path (argc = %d)\n", argc);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+ break;
+ default:
+ break;
+ }
+
if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
if (udid) {
printf("No device found with udid %s.\n", udid);
} else {
printf("No device found.\n");
}
- return -1;
+ return 1;
}
if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
@@ -208,19 +268,24 @@ int main(int argc, char **argv)
goto leave;
}
- plist_t pver = NULL;
- char *product_version = NULL;
- lockdownd_get_value(lckd, NULL, "ProductVersion", &pver);
- if (pver && plist_get_node_type(pver) == PLIST_STRING) {
- plist_get_string_val(pver, &product_version);
- }
+ unsigned int device_version = idevice_get_device_version(device);
+
disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC;
- int product_version_major = 0;
- int product_version_minor = 0;
- if (product_version) {
- if (sscanf(product_version, "%d.%d.%*d", &product_version_major, &product_version_minor) == 2) {
- if (product_version_major >= 7)
- disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
+ if (device_version >= IDEVICE_DEVICE_VERSION(7,0,0)) {
+ disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
+ }
+
+ if (device_version >= IDEVICE_DEVICE_VERSION(16,0,0)) {
+ uint8_t dev_mode_status = 0;
+ plist_t val = NULL;
+ ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
+ if (ldret == LOCKDOWN_E_SUCCESS) {
+ plist_get_bool_val(val, &dev_mode_status);
+ plist_free(val);
+ }
+ if (!dev_mode_status) {
+ printf("ERROR: You have to enable Developer Mode on the given device in order to allowing mounting a developer disk image.\n");
+ goto leave;
}
}
@@ -241,7 +306,7 @@ int main(int argc, char **argv)
service = NULL;
}
- if (!list_mode) {
+ if (cmd == CMD_MOUNT) {
struct stat fst;
if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
@@ -263,7 +328,7 @@ int main(int argc, char **argv)
goto leave;
}
image_size = fst.st_size;
- if (stat(image_sig_path, &fst) != 0) {
+ if (device_version < IDEVICE_DEVICE_VERSION(17,0,0) && stat(image_sig_path, &fst) != 0) {
fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
goto leave;
}
@@ -275,41 +340,233 @@ int main(int argc, char **argv)
mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
plist_t result = NULL;
- if (list_mode) {
+ if (cmd == CMD_LIST) {
/* list mounts mode */
if (!imagetype) {
- imagetype = "Developer";
+ if (device_version < IDEVICE_DEVICE_VERSION(17,0,0)) {
+ imagetype = "Developer";
+ } else {
+ imagetype = "Personalized";
+ }
}
err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
res = 0;
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
+ plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
} else {
printf("Error: lookup_image returned %d\n", err);
}
- } else {
- char sig[8192];
+ } else if (cmd == CMD_MOUNT) {
+ unsigned char *sig = NULL;
size_t sig_length = 0;
- FILE *f = fopen(image_sig_path, "rb");
- if (!f) {
- fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
- goto leave;
- }
- sig_length = fread(sig, 1, sizeof(sig), f);
- fclose(f);
- if (sig_length == 0) {
- fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
- goto leave;
- }
+ FILE *f;
+ struct stat fst;
+ plist_t mount_options = NULL;
- f = fopen(image_path, "rb");
- if (!f) {
- fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
- goto leave;
+ if (device_version < IDEVICE_DEVICE_VERSION(17,0,0)) {
+ f = fopen(image_sig_path, "rb");
+ if (!f) {
+ fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
+ goto leave;
+ }
+ if (fstat(fileno(f), &fst) != 0) {
+ fprintf(stderr, "Error: fstat: %s\n", strerror(errno));
+ goto leave;
+ }
+ sig = malloc(fst.st_size);
+ sig_length = fread(sig, 1, fst.st_size, f);
+ fclose(f);
+ if (sig_length == 0) {
+ fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
+ goto leave;
+ }
+
+ f = fopen(image_path, "rb");
+ if (!f) {
+ fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
+ goto leave;
+ }
+ } else {
+ if (stat(image_path, &fst) != 0) {
+ fprintf(stderr, "Error: stat: '%s': %s\n", image_path, strerror(errno));
+ goto leave;
+ }
+ if (!S_ISDIR(fst.st_mode)) {
+ fprintf(stderr, "Error: Personalized Disk Image mount expects a directory as image path.\n");
+ goto leave;
+ }
+ char* build_manifest_path = string_build_path(image_path, "BuildManifest.plist", NULL);
+ plist_t build_manifest = NULL;
+ if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != 0) {
+ free(build_manifest_path);
+ build_manifest_path = string_build_path(image_path, "Restore", "BuildManifest.plist", NULL);
+ if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) == 0) {
+ char* image_path_new = string_build_path(image_path, "Restore", NULL);
+ free(image_path);
+ image_path = image_path_new;
+ }
+ }
+ if (!build_manifest) {
+ fprintf(stderr, "Error: Could not locate BuildManifest.plist inside given disk image path!\n");
+ goto leave;
+ }
+
+ plist_t identifiers = NULL;
+ mobile_image_mounter_error_t merr = mobile_image_mounter_query_personalization_identifiers(mim, NULL, &identifiers);
+ if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ fprintf(stderr, "Failed to query personalization identifiers: %d\n", merr);
+ goto error_out;
+ }
+
+ unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId");
+ unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID");
+
+ plist_t build_identities = plist_dict_get_item(build_manifest, "BuildIdentities");
+ plist_array_iter iter;
+ plist_array_new_iter(build_identities, &iter);
+ plist_t item = NULL;
+ plist_t build_identity = NULL;
+ do {
+ plist_array_next_item(build_identities, iter, &item);
+ if (!item) {
+ break;
+ }
+ unsigned int bi_board_id = (unsigned int)plist_dict_get_uint(item, "ApBoardID");
+ unsigned int bi_chip_id = (unsigned int)plist_dict_get_uint(item, "ApChipID");
+ if (bi_chip_id == chip_id && bi_board_id == board_id) {
+ build_identity = item;
+ break;
+ }
+ } while (item);
+ plist_mem_free(iter);
+ if (!build_identity) {
+ fprintf(stderr, "Error: The given disk image is not compatible with the current device.\n");
+ goto leave;
+ }
+ plist_t p_tc_path = plist_access_path(build_identity, 4, "Manifest", "LoadableTrustCache", "Info", "Path");
+ if (!p_tc_path) {
+ fprintf(stderr, "Error: Could not determine path for trust cache!\n");
+ goto leave;
+ }
+ plist_t p_dmg_path = plist_access_path(build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path");
+ if (!p_dmg_path) {
+ fprintf(stderr, "Error: Could not determine path for disk image!\n");
+ goto leave;
+ }
+ char *tc_path = string_build_path(image_path, plist_get_string_ptr(p_tc_path, NULL), NULL);
+ unsigned char* trust_cache = NULL;
+ uint64_t trust_cache_size = 0;
+ if (!buffer_read_from_filename(tc_path, (char**)&trust_cache, &trust_cache_size)) {
+ fprintf(stderr, "Error: Trust cache does not exist at '%s'!\n", tc_path);
+ goto leave;
+ }
+ mount_options = plist_new_dict();
+ plist_dict_set_item(mount_options, "ImageTrustCache", plist_new_data((char*)trust_cache, trust_cache_size));
+ free(trust_cache);
+ char *dmg_path = string_build_path(image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL);
+ free(image_path);
+ image_path = dmg_path;
+ f = fopen(image_path, "rb");
+ if (!f) {
+ fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
+ goto leave;
+ }
+
+ unsigned char buf[8192];
+ unsigned char sha384_digest[48];
+ sha384_context ctx;
+ sha384_init(&ctx);
+ fstat(fileno(f), &fst);
+ image_size = fst.st_size;
+ while (!feof(f)) {
+ ssize_t fr = fread(buf, 1, sizeof(buf), f);
+ if (fr <= 0) {
+ break;
+ }
+ sha384_update(&ctx, buf, fr);
+ }
+ rewind(f);
+ sha384_final(&ctx, sha384_digest);
+ unsigned char* manifest = NULL;
+ unsigned int manifest_size = 0;
+ /* check if the device already has a personalization manifest for this image */
+ if (mobile_image_mounter_query_personalization_manifest(mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ printf("Using existing personalization manifest from device.\n");
+ } else {
+ /* we need to re-connect in this case */
+ mobile_image_mounter_free(mim);
+ mim = NULL;
+ if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ goto error_out;
+ }
+ printf("No personalization manifest, requesting from TSS...\n");
+ unsigned char* nonce = NULL;
+ unsigned int nonce_size = 0;
+
+ /* create new TSS request and fill parameters */
+ plist_t request = tss_request_new(NULL);
+ plist_t params = plist_new_dict();
+ tss_parameters_add_from_manifest(params, build_identity, 1);
+
+ /* copy all `Ap,*` items from identifiers */
+ plist_dict_iter di = NULL;
+ plist_dict_new_iter(identifiers, &di);
+ plist_t node = NULL;
+ do {
+ char* key = NULL;
+ plist_dict_next_item(identifiers, di, &key, &node);
+ if (node) {
+ if (!strncmp(key, "Ap,", 3)) {
+ plist_dict_set_item(request, key, plist_copy(node));
+ }
+ }
+ free(key);
+ } while (node);
+ plist_mem_free(di);
+
+ plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID");
+ plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1));
+ plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1));
+ plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1));
+
+ /* query nonce from image mounter service */
+ merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", &nonce, &nonce_size);
+ if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ plist_dict_set_item(params, "ApNonce", plist_new_data((char*)nonce, nonce_size));
+ } else {
+ fprintf(stderr, "ERROR: Failed to query nonce for developer disk image: %d\n", merr);
+ goto error_out;
+ }
+ mobile_image_mounter_free(mim);
+ mim = NULL;
+
+ plist_dict_set_item(params, "ApSepNonce", plist_new_data("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20));
+ plist_dict_set_item(params, "UID_MODE", plist_new_bool(0));
+ tss_request_add_ap_tags(request, params, NULL);
+ tss_request_add_common_tags(request, params, NULL);
+ tss_request_add_ap_img4_tags(request, params);
+ plist_free(params);
+
+ /* request IM4M from TSS */
+ plist_t response = tss_request_send(request, NULL);
+ plist_free(request);
+
+ plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket");
+ if (!PLIST_IS_DATA(p_manifest)) {
+ fprintf(stderr, "Failed to get Img4Ticket\n");
+ goto error_out;
+ }
+
+ uint64_t m4m_len = 0;
+ plist_get_data_val(p_manifest, (char**)&manifest, &m4m_len);
+ manifest_size = m4m_len;
+ plist_free(response);
+ printf("Done.\n");
+ }
+ sig = manifest;
+ sig_length = manifest_size;
+
+ imagetype = "Personalized";
}
char *targetname = NULL;
@@ -323,11 +580,16 @@ int main(int argc, char **argv)
goto leave;
}
-
if (!imagetype) {
imagetype = "Developer";
}
+ if (!mim) {
+ if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ goto error_out;
+ }
+ }
+
switch(disk_image_upload_type) {
case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
printf("Uploading %s\n", image_path);
@@ -336,20 +598,13 @@ int main(int argc, char **argv)
case DISK_IMAGE_UPLOAD_TYPE_AFC:
default:
printf("Uploading %s --> afc:///%s\n", image_path, targetname);
- char **strs = NULL;
- if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
+ plist_t fileinfo = NULL;
+ if (afc_get_file_info_plist(afc, PKG_PATH, &fileinfo) != AFC_E_SUCCESS) {
if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
}
}
- if (strs) {
- int i = 0;
- while (strs[i]) {
- free(strs[i]);
- i++;
- }
- free(strs);
- }
+ plist_free(fileinfo);
uint64_t af = 0;
if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
@@ -402,7 +657,7 @@ int main(int argc, char **argv)
printf("done.\n");
printf("Mounting...\n");
- err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
+ err = mobile_image_mounter_mount_image_with_options(mim, mountname, sig, sig_length, imagetype, mount_options, &result);
if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
if (result) {
plist_t node = plist_dict_get_item(result, "Status");
@@ -415,20 +670,12 @@ int main(int argc, char **argv)
res = 0;
} else {
printf("unexpected status value:\n");
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
+ plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
}
free(status);
} else {
printf("unexpected result:\n");
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
+ plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
}
}
node = plist_dict_get_item(result, "Error");
@@ -440,25 +687,51 @@ int main(int argc, char **argv)
free(error);
} else {
printf("unexpected result:\n");
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
- }
+ plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
}
-
- } else {
- if (xml_mode) {
- print_xml(result);
- } else {
- plist_print_to_stream(result, stdout);
+ node = plist_dict_get_item(result, "DetailedError");
+ if (node) {
+ printf("DetailedError: %s\n", plist_get_string_ptr(node, NULL));
}
+ } else {
+ plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
}
}
} else {
printf("Error: mount_image returned %d\n", err);
}
+ } else if (cmd == CMD_UNMOUNT) {
+ err = mobile_image_mounter_unmount_image(mim, argv[0]);
+ switch (err) {
+ case MOBILE_IMAGE_MOUNTER_E_SUCCESS:
+ printf("Success\n");
+ res = 0;
+ break;
+ case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED:
+ printf("Error: '%s' is not mounted\n", argv[0]);
+ res = 1;
+ break;
+ case MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED:
+ printf("Error: 'unmount' is not supported on this device\n");
+ res = 1;
+ break;
+ case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED:
+ printf("Error: device is locked\n");
+ res = 1;
+ break;
+ default:
+ printf("Error: unmount returned %d\n", err);
+ break;
+ }
+ } else if (cmd == CMD_DEVMODESTATUS) {
+ err = mobile_image_mounter_query_developer_mode_status(mim, &result);
+ if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ res = 0;
+ plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
+ } else {
+ printf("Error: query_developer_mode_status returned %d\n", err);
+ }
}
if (result) {
@@ -481,7 +754,7 @@ leave:
idevice_free(device);
if (image_path)
- free(image_path);
+ free(image_path);
if (image_sig_path)
free(image_sig_path);
diff --git a/tools/ideviceinfo.c b/tools/ideviceinfo.c
index fc0527d..20cc916 100644
--- a/tools/ideviceinfo.c
+++ b/tools/ideviceinfo.c
@@ -31,13 +31,13 @@
#include <errno.h>
#include <stdlib.h>
#include <getopt.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
-#include <libimobiledevice-glue/utils.h>
+#include <plist/plist.h>
#define FORMAT_KEY_VALUE 1
#define FORMAT_XML 2
@@ -152,7 +152,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
@@ -241,11 +241,11 @@ int main(int argc, char *argv[])
free(xml_doc);
break;
case FORMAT_KEY_VALUE:
- plist_print_to_stream(node, stdout);
+ plist_write_to_stream(node, stdout, PLIST_FORMAT_LIMD, 0);
break;
default:
if (key != NULL)
- plist_print_to_stream(node, stdout);
+ plist_write_to_stream(node, stdout, PLIST_FORMAT_LIMD, 0);
break;
}
plist_free(node);
diff --git a/tools/idevicename.c b/tools/idevicename.c
index 69b76f6..248bda3 100644
--- a/tools/idevicename.c
+++ b/tools/idevicename.c
@@ -30,7 +30,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -72,7 +72,7 @@ int main(int argc, char** argv)
const char* udid = NULL;
int use_network = 0;
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
diff --git a/tools/idevicenotificationproxy.c b/tools/idevicenotificationproxy.c
index d1e25c1..192192a 100644
--- a/tools/idevicenotificationproxy.c
+++ b/tools/idevicenotificationproxy.c
@@ -2,7 +2,8 @@
* idevicenotificationproxy.c
* Simple client for the notification_proxy service
*
- * Copyright (c) 2009-2015 Martin Szulecki All Rights Reserved.
+ * Copyright (c) 2018-2024 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2009-2015 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
@@ -32,7 +33,7 @@
#include <errno.h>
#include <signal.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#else
@@ -75,6 +76,7 @@ static void print_usage(int argc, char **argv, int is_error)
"\n"
"The following OPTIONS are accepted:\n"
" -u, --udid UDID target specific device by UDID\n"
+ " -i, --insecure use insecure notification proxy (non-paired device)\n"
" -n, --network connect to network device\n"
" -d, --debug enable communication debugging\n"
" -h, --help prints usage information\n"
@@ -102,6 +104,7 @@ int main(int argc, char *argv[])
int i = 0;
const char* udid = NULL;
int use_network = 0;
+ int insecure = 0;
int cmd = CMD_NONE;
char* cmd_arg = NULL;
@@ -114,6 +117,7 @@ int main(int argc, char *argv[])
{ "debug", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "udid", required_argument, NULL, 'u' },
+ { "insecure", no_argument, NULL, 'i' },
{ "network", no_argument, NULL, 'n' },
{ "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0}
@@ -121,13 +125,13 @@ int main(int argc, char *argv[])
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
- while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "dhu:inv", longopts, NULL)) != -1) {
switch (c) {
case 'd':
idevice_set_debug_level(1);
@@ -143,6 +147,9 @@ int main(int argc, char *argv[])
case 'n':
use_network = 1;
break;
+ case 'i':
+ insecure = 1;
+ break;
case 'h':
print_usage(argc, argv, 0);
return 0;
@@ -214,12 +221,17 @@ int main(int argc, char *argv[])
goto cleanup;
}
- if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
- fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ret);
+ if (insecure) {
+ ret = lockdownd_client_new(device, &client, TOOL_NAME);
+ } else {
+ ret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME);
+ }
+ if (LOCKDOWN_E_SUCCESS != ret) {
+ fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ret), ret);
goto cleanup;
}
- ret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
+ ret = lockdownd_start_service(client, (insecure) ? "com.apple.mobile.insecure_notification_proxy" : NP_SERVICE_NAME, &service);
lockdownd_client_free(client);
diff --git a/tools/idevicepair.c b/tools/idevicepair.c
index dda02ec..884c690 100644
--- a/tools/idevicepair.c
+++ b/tools/idevicepair.c
@@ -32,7 +32,7 @@
#include <getopt.h>
#include <ctype.h>
#include <unistd.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#else
@@ -41,7 +41,6 @@
#endif
#include "common/userpref.h"
-#include <libimobiledevice-glue/utils.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
@@ -51,7 +50,7 @@ static char *udid = NULL;
#ifdef HAVE_WIRELESS_PAIRING
-#ifdef WIN32
+#ifdef _WIN32
#define BS_CC '\b'
#define my_getch getch
#else
@@ -104,7 +103,7 @@ static void pairing_cb(lockdownd_cu_pairing_cb_type_t cb_type, void *user_data,
printf("\n");
} else if (cb_type == LOCKDOWN_CU_PAIRING_DEVICE_INFO) {
printf("Device info:\n");
- plist_print_to_stream_with_indentation((plist_t)data_ptr, stdout, 2);
+ plist_write_to_stream((plist_t)data_ptr, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_INDENT | PLIST_OPT_INDENT_BY(2));
} else if (cb_type == LOCKDOWN_CU_PAIRING_ERROR) {
printf("ERROR: %s\n", (data_ptr) ? (char*)data_ptr : "(unknown)");
}
@@ -257,7 +256,7 @@ int main(int argc, char **argv)
goto leave;
}
if (*optarg == '@') {
- plist_read_from_filename(&host_info_plist, optarg+1);
+ plist_read_from_file(optarg+1, &host_info_plist, NULL);
if (!host_info_plist) {
fprintf(stderr, "ERROR: Could not read from file '%s'\n", optarg+1);
result = EXIT_FAILURE;
@@ -294,7 +293,7 @@ int main(int argc, char **argv)
}
}
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
diff --git a/tools/ideviceprovision.c b/tools/ideviceprovision.c
index 7cd4f3f..94f4ec5 100644
--- a/tools/ideviceprovision.c
+++ b/tools/ideviceprovision.c
@@ -32,11 +32,12 @@
#include <getopt.h>
#include <sys/stat.h>
#include <errno.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
+#include <winsock2.h>
#include <windows.h>
#else
#include <arpa/inet.h>
@@ -45,7 +46,7 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/misagent.h>
-#include <libimobiledevice-glue/utils.h>
+#include <plist/plist.h>
static void print_usage(int argc, char **argv, int is_error)
{
@@ -314,7 +315,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
@@ -436,7 +437,7 @@ int main(int argc, char *argv[])
}
} else {
if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
- plist_print_to_stream(pl, stdout);
+ plist_write_to_stream(pl, stdout, PLIST_FORMAT_LIMD, 0);
} else {
fprintf(stderr, "ERROR: unexpected node type in profile plist (not PLIST_DICT)\n");
res = -1;
@@ -475,27 +476,7 @@ int main(int argc, char *argv[])
return -1;
}
- plist_t pver = NULL;
- char *pver_s = NULL;
- lockdownd_get_value(client, NULL, "ProductVersion", &pver);
- if (pver && plist_get_node_type(pver) == PLIST_STRING) {
- plist_get_string_val(pver, &pver_s);
- }
- plist_free(pver);
- int product_version_major = 0;
- int product_version_minor = 0;
- int product_version_patch = 0;
- if (pver_s) {
- sscanf(pver_s, "%d.%d.%d", &product_version_major, &product_version_minor, &product_version_patch);
- free(pver_s);
- }
- if (product_version_major == 0) {
- fprintf(stderr, "ERROR: Could not determine the device's ProductVersion\n");
- lockdownd_client_free(client);
- idevice_free(device);
- return -1;
- }
- int product_version = ((product_version_major & 0xFF) << 16) | ((product_version_minor & 0xFF) << 8) | (product_version_patch & 0xFF);
+ unsigned int device_version = idevice_get_device_version(device);
lockdownd_error_t lerr = lockdownd_start_service(client, MISAGENT_SERVICE_NAME, &service);
if (lerr != LOCKDOWN_E_SUCCESS) {
@@ -546,7 +527,7 @@ int main(int argc, char *argv[])
{
plist_t profiles = NULL;
misagent_error_t merr;
- if (product_version < 0x090300) {
+ if (device_version < IDEVICE_DEVICE_VERSION(9,3,0)) {
merr = misagent_copy(mis, &profiles);
} else {
merr = misagent_copy_all(mis, &profiles);
@@ -631,7 +612,7 @@ int main(int argc, char *argv[])
/* remove all provisioning profiles */
plist_t profiles = NULL;
misagent_error_t merr;
- if (product_version < 0x090300) {
+ if (device_version < IDEVICE_DEVICE_VERSION(9,3,0)) {
merr = misagent_copy(mis, &profiles);
} else {
merr = misagent_copy_all(mis, &profiles);
diff --git a/tools/idevicescreenshot.c b/tools/idevicescreenshot.c
index 0e694c7..bfaa059 100644
--- a/tools/idevicescreenshot.c
+++ b/tools/idevicescreenshot.c
@@ -32,7 +32,7 @@
#include <errno.h>
#include <time.h>
#include <unistd.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -142,7 +142,7 @@ int main(int argc, char **argv)
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/idevicesetlocation.c b/tools/idevicesetlocation.c
index 69fbaf5..dca8830 100644
--- a/tools/idevicesetlocation.c
+++ b/tools/idevicesetlocation.c
@@ -113,7 +113,7 @@ int main(int argc, char **argv)
if ((argc > 2) || (argc < 1)) {
print_usage(argc+optind, argv-optind, 1);
- return -1;
+ return 1;
}
if (argc == 2) {
@@ -123,7 +123,7 @@ int main(int argc, char **argv)
mode = RESET_LOCATION;
} else {
print_usage(argc+optind, argv-optind, 1);
- return -1;
+ return 1;
}
}
@@ -135,19 +135,30 @@ int main(int argc, char **argv)
} else {
printf("ERROR: No device found!\n");
}
- return -1;
+ return 1;
}
- lockdownd_client_t lockdown;
- lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
+ lockdownd_client_t lockdown = NULL;
+ lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ idevice_free(device);
+ printf("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(lerr), lerr);
+ return 1;
+ }
lockdownd_service_descriptor_t svc = NULL;
- lockdownd_error_t lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc);
+ lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc);
if (lerr != LOCKDOWN_E_SUCCESS) {
+ unsigned int device_version = idevice_get_device_version(device);
lockdownd_client_free(lockdown);
idevice_free(device);
- printf("ERROR: Could not start the simulatelocation service: %s\nMake sure a developer disk image is mounted!\n", lockdownd_strerror(lerr));
- return -1;
+ printf("ERROR: Could not start the simulatelocation service: %s\n", lockdownd_strerror(lerr));
+ if (device_version >= IDEVICE_DEVICE_VERSION(17,0,0)) {
+ printf("Note: This tool is currently not supported on iOS 17+\n");
+ } else {
+ printf("Make sure a developer disk image is mounted!\n");
+ }
+ return 1;
}
lockdownd_client_free(lockdown);
@@ -158,10 +169,9 @@ int main(int argc, char **argv)
lockdownd_service_descriptor_free(svc);
if (serr != SERVICE_E_SUCCESS) {
- lockdownd_client_free(lockdown);
idevice_free(device);
printf("ERROR: Could not connect to simulatelocation service (%d)\n", serr);
- return -1;
+ return 1;
}
uint32_t l;
diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c
index 3084b97..88af4c1 100644
--- a/tools/idevicesyslog.c
+++ b/tools/idevicesyslog.c
@@ -33,8 +33,9 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
+#include <time.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#endif
@@ -42,10 +43,12 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/syslog_relay.h>
#include <libimobiledevice-glue/termcolors.h>
+#include <libimobiledevice/ostrace.h>
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;
@@ -58,6 +61,9 @@ 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;
@@ -66,11 +72,16 @@ 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;
@@ -129,6 +140,70 @@ static int find_char(char c, char** p, const char* end)
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) {
@@ -149,7 +224,7 @@ static void syslog_callback(char c, void *user_data)
do {
if (lp < 16) {
shall_print = 1;
- cprintf(COLOR_WHITE);
+ cprintf(FG_WHITE);
break;
}
@@ -202,20 +277,9 @@ static void syslog_callback(char c, void *user_data)
}
/* 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;
- }
- shall_print = 1;
+ shall_print = message_filter_matching(device_name_end+1);
+ if (!shall_print) {
+ break;
}
/* process name */
@@ -235,39 +299,10 @@ static void syslog_callback(char c, void *user_data)
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) {
+ /* 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) {
@@ -282,35 +317,35 @@ static void syslog_callback(char c, void *user_data)
const char* level_color = NULL;
if (!strncmp(p, "<Notice>:", 9)) {
level_end += 9;
- level_color = COLOR_GREEN;
+ level_color = FG_GREEN;
} else if (!strncmp(p, "<Error>:", 8)) {
level_end += 8;
- level_color = COLOR_RED;
+ level_color = FG_RED;
} else if (!strncmp(p, "<Warning>:", 10)) {
level_end += 10;
- level_color = COLOR_YELLOW;
+ level_color = FG_YELLOW;
} else if (!strncmp(p, "<Debug>:", 8)) {
level_end += 8;
- level_color = COLOR_MAGENTA;
+ level_color = FG_MAGENTA;
} else {
- level_color = COLOR_WHITE;
+ level_color = FG_WHITE;
}
/* write date and time */
- cprintf(COLOR_LIGHT_GRAY);
+ cprintf(FG_LIGHT_GRAY);
fwrite(line, 1, 16, stdout);
if (show_device_name) {
/* write device name */
- cprintf(COLOR_DARK_YELLOW);
+ cprintf(FG_DARK_YELLOW);
fwrite(device_name_start, 1, device_name_end-device_name_start+1, stdout);
cprintf(COLOR_RESET);
}
/* write process name */
- cprintf(COLOR_BRIGHT_CYAN);
+ cprintf(FG_BRIGHT_CYAN);
fwrite(process_name_start, 1, process_name_end-process_name_start, stdout);
- cprintf(COLOR_CYAN);
+ cprintf(FG_CYAN);
fwrite(process_name_end, 1, proc_name_end-process_name_end+1, stdout);
/* write log level */
@@ -323,15 +358,15 @@ static void syslog_callback(char c, void *user_data)
lp -= p - linep;
linep = p;
- cprintf(COLOR_WHITE);
+ cprintf(FG_WHITE);
} else {
shall_print = 1;
- cprintf(COLOR_WHITE);
+ cprintf(FG_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) {
+ 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);
@@ -345,12 +380,231 @@ static void syslog_callback(char c, void *user_data)
}
}
-static int start_logging(void)
+static void ostrace_syslog_callback(const void* buf, size_t len, void* user_data)
{
- 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;
+ 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;
@@ -361,14 +615,28 @@ static int start_logging(void)
device = NULL;
return -1;
}
-
- /* start syslog_relay service */
lockdownd_service_descriptor_t svc = NULL;
- lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc);
+
+ 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, SYSLOG_RELAY_SERVICE_NAME, &svc);
+ lerr = lockdownd_start_service(lockdown, service_name, &svc);
if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) {
break;
}
@@ -376,32 +644,84 @@ static int start_logging(void)
}
}
if (lerr != LOCKDOWN_E_SUCCESS) {
- fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr);
+ 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);
- /* 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 start service com.apple.syslog_relay.\n");
- idevice_free(device);
- device = NULL;
+ 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 */
- 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);
- syslog = NULL;
- idevice_free(device);
- device = NULL;
+ 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;
}
@@ -419,6 +739,11 @@ static void stop_logging(void)
syslog_relay_client_free(syslog);
syslog = NULL;
}
+ if (ostrace) {
+ ostrace_stop_activity(ostrace);
+ ostrace_client_free(ostrace);
+ ostrace = NULL;
+ }
if (device) {
idevice_free(device);
@@ -426,6 +751,77 @@ static void stop_logging(void)
}
}
+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) {
@@ -435,7 +831,7 @@ static void device_event_cb(const idevice_event_t* event, void* userdata)
return;
}
if (event->event == IDEVICE_DEVICE_ADD) {
- if (!syslog) {
+ if (!syslog && !ostrace) {
if (!udid) {
udid = strdup(event->udid);
}
@@ -446,7 +842,7 @@ static void device_event_cb(const idevice_event_t* event, void* userdata)
}
}
} else if (event->event == IDEVICE_DEVICE_REMOVE) {
- if (syslog && (strcmp(udid, event->udid) == 0)) {
+ if ((syslog || ostrace) && (strcmp(udid, event->udid) == 0)) {
stop_logging();
fprintf(stdout, "[disconnected:%s]\n", udid);
if (exit_on_disconnect) {
@@ -480,10 +876,24 @@ static void print_usage(int argc, char **argv, int is_error)
" -h, --help prints usage information\n"
" -d, --debug enable communication debugging\n"
" -v, --version prints version information\n"
- " --no-colors disable colored output\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"
@@ -508,6 +918,7 @@ int main(int argc, char *argv[])
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' },
@@ -525,18 +936,26 @@ int main(int argc, char *argv[])
{ "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
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
- while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:e:p:qkKv", longopts, NULL)) != -1) {
+ 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);
@@ -587,6 +1006,22 @@ int main(int argc, char *argv[])
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");
@@ -638,6 +1073,34 @@ int main(int argc, char *argv[])
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;
@@ -647,6 +1110,10 @@ int main(int argc, char *argv[])
}
}
+ 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);
@@ -693,14 +1160,92 @@ int main(int argc, char *argv[])
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 (num == 0) {
+ 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, "No device found. Plug in a device or pass UDID with -u to wait for device to become available.\n");
+ return 1;
}
fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
@@ -735,6 +1280,13 @@ int main(int argc, char *argv[])
}
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++) {