summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am139
-rw-r--r--tools/afcclient.c1346
-rw-r--r--tools/idevice_id.c51
-rw-r--r--tools/idevicebackup.c383
-rw-r--r--tools/idevicebackup2.c591
-rw-r--r--tools/idevicebtlogger.c458
-rw-r--r--tools/idevicecrashreport.c175
-rw-r--r--tools/idevicedate.c152
-rw-r--r--tools/idevicedebug.c663
-rw-r--r--tools/idevicedebugserverproxy.c339
-rw-r--r--tools/idevicedevmodectl.c462
-rw-r--r--tools/idevicediagnostics.c267
-rw-r--r--tools/ideviceenterrecovery.c100
-rw-r--r--tools/ideviceimagemounter.c149
-rw-r--r--tools/ideviceinfo.c84
-rw-r--r--tools/idevicename.c79
-rw-r--r--tools/idevicenotificationproxy.c183
-rw-r--r--tools/idevicepair.c386
-rw-r--r--tools/ideviceprovision.c256
-rw-r--r--tools/idevicescreenshot.c215
-rw-r--r--tools/idevicesetlocation.c192
-rw-r--r--tools/idevicesyslog.c579
22 files changed, 5492 insertions, 1757 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index db929bb..4cac1fc 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,91 +1,144 @@
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)
-
-AM_CFLAGS = $(GLOBAL_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgcrypt_CFLAGS) $(openssl_CFLAGS) $(libplist_CFLAGS) $(LFS_CFLAGS)
-AM_LDFLAGS = $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgcrypt_LIBS) $(openssl_LIBS) $(libplist_LIBS)
-
-bin_PROGRAMS = idevice_id ideviceinfo idevicename idevicepair idevicesyslog ideviceimagemounter idevicescreenshot ideviceenterrecovery idevicedate idevicebackup idevicebackup2 ideviceprovision idevicedebugserverproxy idevicediagnostics idevicedebug idevicenotificationproxy idevicecrashreport
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)
+
+AM_CFLAGS = \
+ $(GLOBAL_CFLAGS) \
+ $(ssl_lib_CFLAGS) \
+ $(libplist_CFLAGS) \
+ $(LFS_CFLAGS)
+
+AM_LDFLAGS = \
+ $(libplist_LIBS)
+
+bin_PROGRAMS = \
+ idevicebtlogger\
+ idevice_id \
+ ideviceinfo \
+ idevicename \
+ idevicepair \
+ idevicesyslog \
+ ideviceimagemounter \
+ idevicescreenshot \
+ ideviceenterrecovery \
+ idevicedate \
+ idevicebackup \
+ idevicebackup2 \
+ ideviceprovision \
+ idevicedebugserverproxy \
+ idevicediagnostics \
+ idevicedebug \
+ idevicedevmodectl \
+ idevicenotificationproxy \
+ idevicecrashreport \
+ idevicesetlocation \
+ afcclient
+
+idevicebtlogger_SOURCES = idevicebtlogger.c
+iidevicebtlogger_CFLAGS = $(AM_CFLAGS)
+idevicebtlogger_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
+idevicebtlogger_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
ideviceinfo_SOURCES = ideviceinfo.c
-ideviceinfo_CFLAGS = $(AM_CFLAGS)
-ideviceinfo_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
-ideviceinfo_LDADD = $(top_builddir)/src/libimobiledevice.la
+ideviceinfo_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+ideviceinfo_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+ideviceinfo_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicename_SOURCES = idevicename.c
idevicename_CFLAGS = $(AM_CFLAGS)
idevicename_LDFLAGS = $(AM_LDFLAGS)
-idevicename_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicename_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicepair_SOURCES = idevicepair.c
-idevicepair_CFLAGS = -I$(top_srcdir) $(AM_CFLAGS)
-idevicepair_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS) $(libusbmuxd_LIBS)
-idevicepair_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicepair_CFLAGS = $(AM_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)
idevicesyslog_SOURCES = idevicesyslog.c
-idevicesyslog_CFLAGS = $(AM_CFLAGS)
-idevicesyslog_LDFLAGS = $(AM_LDFLAGS)
-idevicesyslog_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicesyslog_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+idevicesyslog_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+idevicesyslog_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevice_id_SOURCES = idevice_id.c
idevice_id_CFLAGS = $(AM_CFLAGS)
idevice_id_LDFLAGS = $(AM_LDFLAGS)
-idevice_id_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevice_id_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicebackup_SOURCES = idevicebackup.c
-idevicebackup_CFLAGS = $(AM_CFLAGS)
-idevicebackup_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
-idevicebackup_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicebackup_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+idevicebackup_LDFLAGS = $(AM_LDFLAGS) $(ssl_lib_LIBS) $(limd_glue_LIBS)
+idevicebackup_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicebackup2_SOURCES = idevicebackup2.c
-idevicebackup2_CFLAGS = $(AM_CFLAGS)
-idevicebackup2_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
-idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicebackup2_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+idevicebackup2_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
ideviceimagemounter_SOURCES = ideviceimagemounter.c
-ideviceimagemounter_CFLAGS = $(AM_CFLAGS)
-ideviceimagemounter_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
-ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice.la
+ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicescreenshot_SOURCES = idevicescreenshot.c
idevicescreenshot_CFLAGS = $(AM_CFLAGS)
idevicescreenshot_LDFLAGS = $(AM_LDFLAGS)
-idevicescreenshot_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicescreenshot_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
ideviceenterrecovery_SOURCES = ideviceenterrecovery.c
ideviceenterrecovery_CFLAGS = $(AM_CFLAGS)
ideviceenterrecovery_LDFLAGS = $(AM_LDFLAGS)
-ideviceenterrecovery_LDADD = $(top_builddir)/src/libimobiledevice.la
+ideviceenterrecovery_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicedate_SOURCES = idevicedate.c
idevicedate_CFLAGS = $(AM_CFLAGS)
idevicedate_LDFLAGS = $(AM_LDFLAGS)
-idevicedate_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicedate_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
ideviceprovision_SOURCES = ideviceprovision.c
-ideviceprovision_CFLAGS = $(AM_CFLAGS)
-ideviceprovision_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
-ideviceprovision_LDADD = $(top_builddir)/src/libimobiledevice.la
+ideviceprovision_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+ideviceprovision_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+ideviceprovision_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicedebugserverproxy_SOURCES = idevicedebugserverproxy.c
-idevicedebugserverproxy_CFLAGS = -I$(top_srcdir) $(AM_CFLAGS)
-idevicedebugserverproxy_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
-idevicedebugserverproxy_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicedebugserverproxy_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+idevicedebugserverproxy_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+idevicedebugserverproxy_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicediagnostics_SOURCES = idevicediagnostics.c
idevicediagnostics_CFLAGS = $(AM_CFLAGS)
idevicediagnostics_LDFLAGS = $(AM_LDFLAGS)
-idevicediagnostics_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicediagnostics_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicedebug_SOURCES = idevicedebug.c
-idevicedebug_CFLAGS = $(AM_CFLAGS)
-idevicedebug_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
-idevicedebug_LDADD = $(top_builddir)/src/libimobiledevice.la
+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)
-idevicenotificationproxy_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicenotificationproxy_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicecrashreport_SOURCES = idevicecrashreport.c
-idevicecrashreport_CFLAGS = -I$(top_srcdir) $(AM_CFLAGS)
-idevicecrashreport_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
-idevicecrashreport_LDADD = $(top_builddir)/src/libimobiledevice.la
+idevicecrashreport_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+idevicecrashreport_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
+idevicecrashreport_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
+
+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)
+afcclient_LDFLAGS = $(AM_LDFLAGS)
+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..9bcd77b
--- /dev/null
+++ b/tools/afcclient.c
@@ -0,0 +1,1346 @@
+/*
+ * 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>
+
+#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>
+
+#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 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 PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n");
+ printf("put 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)
+{
+ char **info = NULL;
+ afc_error_t err = afc_get_device_info(afc, &info);
+ if (err == AFC_E_SUCCESS && info) {
+ int i;
+ for (i = 0; info[i]; i += 2) {
+ printf("%s: %s\n", info[i], info[i+1]);
+ }
+ } else {
+ printf("Error: Failed to get device info: %s (%d)\n", afc_strerror(err), err);
+ }
+ afc_dictionary_free(info);
+}
+
+static int get_file_info_stat(afc_client_t afc, const char* path, struct afc_file_stat *stbuf)
+{
+ char **info = NULL;
+ afc_error_t ret = afc_get_file_info(afc, path, &info);
+ memset(stbuf, 0, sizeof(struct afc_file_stat));
+ if (ret != AFC_E_SUCCESS) {
+ return -1;
+ } else if (!info) {
+ return -1;
+ } else {
+ // get file attributes from info list
+ int i;
+ for (i = 0; info[i]; i += 2) {
+ if (!strcmp(info[i], "st_size")) {
+ stbuf->st_size = atoll(info[i+1]);
+ } else if (!strcmp(info[i], "st_blocks")) {
+ stbuf->st_blocks = atoi(info[i+1]);
+ } else if (!strcmp(info[i], "st_ifmt")) {
+ if (!strcmp(info[i+1], "S_IFREG")) {
+ stbuf->st_mode = S_IFREG;
+ } else if (!strcmp(info[i+1], "S_IFDIR")) {
+ stbuf->st_mode = S_IFDIR;
+ } else if (!strcmp(info[i+1], "S_IFLNK")) {
+ stbuf->st_mode = S_IFLNK;
+ } else if (!strcmp(info[i+1], "S_IFBLK")) {
+ stbuf->st_mode = S_IFBLK;
+ } else if (!strcmp(info[i+1], "S_IFCHR")) {
+ stbuf->st_mode = S_IFCHR;
+ } else if (!strcmp(info[i+1], "S_IFIFO")) {
+ stbuf->st_mode = S_IFIFO;
+ } else if (!strcmp(info[i+1], "S_IFSOCK")) {
+ stbuf->st_mode = S_IFSOCK;
+ }
+ } else if (!strcmp(info[i], "st_nlink")) {
+ stbuf->st_nlink = atoi(info[i+1]);
+ } else if (!strcmp(info[i], "st_mtime")) {
+ stbuf->st_mtime = (time_t)(atoll(info[i+1]) / 1000000000);
+ } else if (!strcmp(info[i], "st_birthtime")) { /* available on iOS 7+ */
+ stbuf->st_birthtime = (time_t)(atoll(info[i+1]) / 1000000000);
+ }
+ }
+ afc_dictionary_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;
+ }
+
+ char **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(afc, abspath, &info);
+ if (err == AFC_E_SUCCESS && info) {
+ int i;
+ for (i = 0; info[i]; i += 2) {
+ printf("%s: %s\n", info[i], info[i+1]);
+ }
+ } else {
+ printf("Error: Failed to get file info for %s: %s (%d)\n", argv[0], afc_strerror(err), err);
+ }
+ afc_dictionary_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 void handle_get(afc_client_t afc, int argc, char** argv)
+{
+ if (argc < 1 || argc > 2) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+ char *srcpath = NULL;
+ char* dstpath = NULL;
+ if (argc == 1) {
+ srcpath = get_absolute_path(argv[0]);
+ dstpath = strdup(path_get_basename(argv[0]));
+ } else {
+ srcpath = get_absolute_path(argv[0]);
+ dstpath = strdup(argv[1]);
+ }
+
+ char **info = NULL;
+ uint64_t file_size = 0;
+ afc_get_file_info(afc, srcpath, &info);
+ if (info) {
+ char **p = info;
+ while (p && *p) {
+ if (!strcmp(*p, "st_size")) {
+ p++;
+ file_size = (uint64_t)strtoull(*p, NULL, 10);
+ break;
+ }
+ p++;
+ }
+ }
+ uint64_t fh = 0;
+ afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh);
+ if (err != AFC_E_SUCCESS) {
+ free(srcpath);
+ free(dstpath);
+ printf("Error: Failed to open file '%s': %s (%d)\n", argv[0], afc_strerror(err), err);
+ return;
+ }
+ FILE *f = fopen(dstpath, "wb");
+ if (!f && errno == EISDIR) {
+ const char* basen = path_get_basename(argv[0]);
+ size_t len = strlen(dstpath) + 1 + strlen(basen) + 1;
+ char* newdst = (char*)malloc(len);
+ snprintf(newdst, len, "%s/%s", dstpath, basen);
+ f = fopen(newdst, "wb");
+ free(newdst);
+ }
+ if (f) {
+ 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);
+ }
+ 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");
+ 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", argv[0], afc_strerror(err), err);
+ }
+ free(buf);
+ fclose(f);
+ } else {
+ printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno));
+ }
+ afc_file_close(afc, fh);
+ free(srcpath);
+}
+
+static void handle_put(afc_client_t afc, int argc, char** argv)
+{
+ if (argc < 1 || argc > 2) {
+ printf("Error: Invalid number of arguments\n");
+ return;
+ }
+
+ char *dstpath = NULL;
+ if (argc == 1) {
+ dstpath = get_absolute_path(path_get_basename(argv[0]));
+ } else {
+ dstpath = get_absolute_path(argv[1]);
+ }
+
+ uint64_t fh = 0;
+ FILE *f = fopen(argv[0], "rb");
+ if (f) {
+ afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh);
+ if (err == AFC_E_OBJECT_IS_DIR) {
+ const char* basen = path_get_basename(argv[0]);
+ size_t len = strlen(dstpath) + 1 + strlen(basen) + 1;
+ char* newdst = (char*)malloc(len);
+ snprintf(newdst, len, "%s/%s", dstpath, basen);
+ free(dstpath);
+ dstpath = get_absolute_path(newdst);
+ free(newdst);
+ err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh);
+ }
+ if (err != AFC_E_SUCCESS) {
+ printf("Error: Failed to open file '%s' on device: %s (%d)\n", argv[1], afc_strerror(err), err);
+ } else {
+ 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;
+ 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");
+ }
+ 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");
+ 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;
+ }
+ }
+ }
+ printf("\n");
+ free(buf);
+ afc_file_close(afc, fh);
+ }
+ fclose(f);
+ } else {
+ printf("Error: Failed to open local file '%s': %s\n", argv[0], strerror(errno));
+ }
+ 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;
+ char **info = NULL;
+ afc_error_t err = afc_get_file_info(afc, path, &info);
+ if (err == AFC_E_SUCCESS && info) {
+ int i;
+ for (i = 0; info[i]; i += 2) {
+ if (!strcmp(info[i], "st_ifmt")) {
+ if (!strcmp(info[i+1], "S_IFDIR")) {
+ is_dir = 1;
+ }
+ break;
+ }
+ }
+ afc_dictionary_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/idevice_id.c b/tools/idevice_id.c
index b746491..540a6f2 100644
--- a/tools/idevice_id.c
+++ b/tools/idevice_id.c
@@ -23,6 +23,8 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevice_id"
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -36,20 +38,25 @@
static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
- name = strrchr(argv[0], '/');
+ char *name = strrchr(argv[0], '/');
fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [UDID]\n", (name ? name + 1: argv[0]));
fprintf(is_error ? stderr : stdout,
- "Prints device name or a list of attached devices.\n\n" \
- " If UDID is given, the name of the connected device with that UDID" \
- " will be retrieved.\n\n" \
- " -l, --list list UDIDs of all devices attached via USB\n" \
- " -n, --network list UDIDs of all devices available via network\n" \
- " -d, --debug enable communication debugging\n" \
- " -h, --help prints usage information\n" \
- "\n" \
- "Homepage: <" PACKAGE_URL ">\n"
- );
+ "\n"
+ "List attached devices or print device name of given device.\n"
+ "\n" \
+ " If UDID is given, the name of the connected device with that UDID"
+ " will be retrieved.\n"
+ "\n" \
+ "OPTIONS:\n"
+ " -l, --list list UDIDs of all devices attached via USB\n"
+ " -n, --network list UDIDs of all devices available via network\n"
+ " -d, --debug enable communication debugging\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
int main(int argc, char **argv)
@@ -60,7 +67,7 @@ int main(int argc, char **argv)
char *device_name = NULL;
int ret = 0;
int i;
- int mode = MODE_SHOW_ID;
+ int mode = MODE_LIST_DEVICES;
int include_usb = 0;
int include_network = 0;
const char* udid = NULL;
@@ -71,10 +78,11 @@ int main(int argc, char **argv)
{ "help", no_argument, NULL, 'h' },
{ "list", no_argument, NULL, 'l' },
{ "network", no_argument, NULL, 'n' },
+ { "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0}
};
- while ((c = getopt_long(argc, argv, "dhln", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "dhlnv", longopts, NULL)) != -1) {
switch (c) {
case 'd':
idevice_set_debug_level(1);
@@ -90,6 +98,9 @@ int main(int argc, char **argv)
mode = MODE_LIST_DEVICES;
include_network = 1;
break;
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ return 0;
default:
print_usage(argc, argv, 1);
exit(EXIT_FAILURE);
@@ -98,9 +109,11 @@ int main(int argc, char **argv)
argc -= optind;
argv += optind;
- if (mode == MODE_SHOW_ID && argc != 1) {
- print_usage(argc + optind, argv - optind, 1);
- exit(EXIT_FAILURE);
+ if (argc == 1) {
+ mode = MODE_SHOW_ID;
+ } else if (argc == 0 && optind == 1) {
+ include_usb = 1;
+ include_network = 1;
}
udid = argv[0];
@@ -112,7 +125,7 @@ int main(int argc, char **argv)
return -2;
}
- if (LOCKDOWN_E_SUCCESS != lockdownd_client_new(device, &client, "idevice_id")) {
+ if (LOCKDOWN_E_SUCCESS != lockdownd_client_new(device, &client, TOOL_NAME)) {
idevice_free(device);
fprintf(stderr, "ERROR: Connecting to device failed!\n");
return -2;
@@ -147,7 +160,7 @@ int main(int argc, char **argv)
printf("%s", dev_list[i]->udid);
if (include_usb && include_network) {
if (dev_list[i]->conn_type == CONNECTION_NETWORK) {
- printf(" (WiFi)");
+ printf(" (Network)");
} else {
printf(" (USB)");
}
diff --git a/tools/idevicebackup.c b/tools/idevicebackup.c
index de08295..5694c12 100644
--- a/tools/idevicebackup.c
+++ b/tools/idevicebackup.c
@@ -24,15 +24,31 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicebackup"
+
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
-#ifdef HAVE_OPENSSL
+#include <getopt.h>
+#if defined(HAVE_OPENSSL)
#include <openssl/sha.h>
-#else
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/evp.h>
+#endif
+#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>
@@ -43,7 +59,8 @@
#include <libimobiledevice/mobilebackup.h>
#include <libimobiledevice/notification_proxy.h>
#include <libimobiledevice/afc.h>
-#include "common/utils.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"
@@ -76,10 +93,12 @@ enum device_link_file_status_t {
static void sha1_of_data(const char *input, uint32_t size, unsigned char *hash_out)
{
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_OPENSSL)
SHA1((const unsigned char*)input, size, hash_out);
-#else
+#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
}
@@ -94,12 +113,34 @@ 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)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_DigestUpdate(context, data, len);
+#else
+ SHA1_Update(context, data, len);
+#endif
+#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)
{
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_OPENSSL)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MD_CTX* sha1 = EVP_MD_CTX_new();
+ EVP_DigestInit(sha1, EVP_sha1());
+ void* psha1 = sha1;
+#else
SHA_CTX sha1;
SHA1_Init(&sha1);
-#else
+ void* psha1 = &sha1;
+#endif
+#elif defined(HAVE_GNUTLS)
gcry_md_hd_t hd = NULL;
gcry_md_open(&hd, GCRY_MD_SHA1, 0);
if (!hd) {
@@ -107,102 +148,68 @@ static void compute_datahash(const char *path, const char *destpath, uint8_t gre
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
FILE *f = fopen(path, "rb");
if (f) {
unsigned char buf[16384];
size_t len;
while ((len = fread(buf, 1, 16384, f)) > 0) {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, buf, len);
-#else
- gcry_md_write(hd, buf, len);
-#endif
+ _sha1_update(psha1, (const char*)buf, len);
}
fclose(f);
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, destpath, strlen(destpath));
- SHA1_Update(&sha1, ";", 1);
-#else
- gcry_md_write(hd, destpath, strlen(destpath));
- gcry_md_write(hd, ";", 1);
-#endif
+ _sha1_update(psha1, destpath, strlen(destpath));
+ _sha1_update(psha1, ";", 1);
+
if (greylist == 1) {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, "true", 4);
-#else
- gcry_md_write(hd, "true", 4);
-#endif
+ _sha1_update(psha1, "true", 4);
} else {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, "false", 5);
-#else
- gcry_md_write(hd, "false", 5);
-#endif
+ _sha1_update(psha1, "false", 5);
}
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, ";", 1);
-#else
- gcry_md_write(hd, ";", 1);
-#endif
+ _sha1_update(psha1, ";", 1);
+
if (domain) {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, domain, strlen(domain));
-#else
- gcry_md_write(hd, domain, strlen(domain));
-#endif
+ _sha1_update(psha1, domain, strlen(domain));
} else {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, "(null)", 6);
-#else
- gcry_md_write(hd, "(null)", 6);
-#endif
+ _sha1_update(psha1, "(null)", 6);
}
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, ";", 1);
-#else
- gcry_md_write(hd, ";", 1);
-#endif
+ _sha1_update(psha1, ";", 1);
+
if (appid) {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, appid, strlen(appid));
-#else
- gcry_md_write(hd, appid, strlen(appid));
-#endif
+ _sha1_update(psha1, appid, strlen(appid));
} else {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, "(null)", 6);
-#else
- gcry_md_write(hd, "(null)", 6);
-#endif
+ _sha1_update(psha1, "(null)", 6);
}
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, ";", 1);
-#else
- gcry_md_write(hd, ";", 1);
-#endif
+ _sha1_update(psha1, ";", 1);
+
if (version) {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, version, strlen(version));
-#else
- gcry_md_write(hd, version, strlen(version));
-#endif
+ _sha1_update(psha1, version, strlen(version));
} else {
-#ifdef HAVE_OPENSSL
- SHA1_Update(&sha1, "(null)", 6);
-#else
- gcry_md_write(hd, "(null)", 6);
-#endif
+ _sha1_update(psha1, "(null)", 6);
}
-#ifdef HAVE_OPENSSL
- SHA1_Final(hash_out, &sha1);
+#if defined(HAVE_OPENSSL)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_DigestFinal(sha1, hash_out, NULL);
+ EVP_MD_CTX_destroy(sha1);
#else
+ SHA1_Final(hash_out, &sha1);
+#endif
+#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
}
-#ifndef HAVE_OPENSSL
+#if defined(HAVE_GNUTLS)
gcry_md_close(hd);
+#elif defined(HAVE_MBEDTLS)
+ mbedtls_sha1_free(&sha1);
#endif
}
@@ -329,7 +336,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;
@@ -343,7 +350,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");
@@ -466,7 +473,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");
@@ -546,7 +553,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
for ( i = 0; i < 20; i++, p += 2 ) {
snprintf (p, 3, "%02x", (unsigned char)fnhash[i] );
}
- if (strcmp(fnamehash, hash)) {
+ if (strcmp(fnamehash, hash) != 0) {
printf("\r\n");
printf("WARNING: filename hash does not match for entry '%s'\n", hash);
}
@@ -557,7 +564,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
plist_get_string_val(node, &auth_version);
}
- if (strcmp(auth_version, "1.0")) {
+ if (strcmp(auth_version, "1.0") != 0) {
printf("\r\n");
printf("WARNING: Unknown AuthVersion '%s', DataHash cannot be verified!\n", auth_version);
}
@@ -606,20 +613,20 @@ static void do_post_notification(const char *notification)
np_client_t np;
if (!client) {
- if (lockdownd_client_new_with_handshake(device, &client, "idevicebackup") != LOCKDOWN_E_SUCCESS) {
+ if (lockdownd_client_new_with_handshake(device, &client, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
return;
}
}
- lockdownd_start_service(client, NP_SERVICE_NAME, &service);
- if (service && service->port) {
+ lockdownd_error_t ldret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
+ if (ldret == LOCKDOWN_E_SUCCESS) {
np_client_new(device, service, &np);
if (np) {
np_post_notification(np, notification);
np_client_free(np);
}
} else {
- printf("Could not start %s\n", NP_SERVICE_NAME);
+ printf("Could not start %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
}
if (service) {
@@ -660,21 +667,28 @@ static void clean_exit(int sig)
quit_flag++;
}
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] CMD [DIRECTORY]\n", (name ? name + 1: argv[0]));
- printf("Create or restore backup from the current or specified directory.\n\n");
- printf("commands:\n");
- printf(" backup\tSaves a device backup into DIRECTORY\n");
- printf(" restore\tRestores a device backup from DIRECTORY.\n\n");
- printf("options:\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD DIRECTORY\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Create or restore backup in/from the specified directory.\n"
+ "\n"
+ "CMD:\n"
+ " backup Saves a device backup into DIRECTORY\n"
+ " restore Restores a device backup from DIRECTORY.\n"
+ "\n"
+ "OPTIONS:\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 prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
int main(int argc, char *argv[])
@@ -683,6 +697,7 @@ int main(int argc, char *argv[])
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
int i;
char* udid = NULL;
+ int use_network = 0;
lockdownd_service_descriptor_t service = NULL;
int cmd = -1;
int is_full_backup = 0;
@@ -697,7 +712,15 @@ int main(int argc, char *argv[])
uint64_t length = 0;
uint64_t backup_total_size = 0;
enum device_link_file_status_t file_status = DEVICE_LINK_FILE_STATUS_NONE;
- uint64_t c = 0;
+ int c = 0;
+ const struct option longopts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+ { "network", no_argument, NULL, 'n' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
+ };
/* we need to exit cleanly on running backups and restores or we cause havok */
signal(SIGINT, clean_exit);
@@ -708,52 +731,59 @@ int main(int argc, char *argv[])
#endif
/* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return 0;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = strdup(argv[i]);
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
+ udid = strdup(optarg);
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'h':
+ print_usage(argc, argv, 0);
return 0;
- }
- else if (!strcmp(argv[i], "backup")) {
- cmd = CMD_BACKUP;
- }
- else if (!strcmp(argv[i], "restore")) {
- cmd = CMD_RESTORE;
- }
- else if (backup_directory == NULL) {
- backup_directory = argv[i];
- }
- else {
- print_usage(argc, argv);
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
return 0;
+ default:
+ print_usage(argc, argv, 1);
+ return 2;
}
}
+ argc -= optind;
+ argv += optind;
- /* verify options */
- if (cmd == -1) {
- printf("No command specified.\n");
- print_usage(argc, argv);
- return -1;
+ if (argc < 1) {
+ fprintf(stderr, "ERROR: Missing command.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- if (backup_directory == NULL) {
- printf("No target backup directory specified.\n");
- print_usage(argc, argv);
- return -1;
+ if (!strcmp(argv[0], "backup")) {
+ cmd = CMD_BACKUP;
+ } else if (!strcmp(argv[0], "restore")) {
+ cmd = CMD_RESTORE;
+ } else {
+ fprintf(stderr, "ERROR: Invalid command '%s'.\n", argv[0]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
+ if (argc < 2) {
+ fprintf(stderr, "No target backup directory specified.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+ backup_directory = argv[1];
+
/* verify if passed backup directory exists */
if (stat(backup_directory, &st) != 0) {
printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
@@ -772,25 +802,24 @@ int main(int argc, char *argv[])
printf("Backup directory is \"%s\"\n", backup_directory);
- if (udid) {
- ret = idevice_new(&device, udid);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found with udid %s, is it plugged in?\n", udid);
- return -1;
+ 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;
}
- else
- {
- ret = idevice_new(&device, NULL);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found, is it plugged in?\n");
- return -1;
- }
+
+ if (!udid) {
+ idevice_get_udid(device, &udid);
}
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, "idevicebackup"))) {
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
idevice_free(device);
+ free(udid);
return -1;
}
@@ -810,6 +839,7 @@ int main(int argc, char *argv[])
printf("ERROR: This tool is only compatible with iOS 3 or below. For newer iOS versions please use the idevicebackup2 tool.\n");
lockdownd_client_free(client);
idevice_free(device);
+ free(udid);
return -1;
}
}
@@ -830,7 +860,7 @@ int main(int argc, char *argv[])
};
np_observe_notifications(np, noties);
} else {
- printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
+ printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
}
afc_client_t afc = NULL;
@@ -838,9 +868,11 @@ int main(int argc, char *argv[])
/* start AFC, we need this for the lock file */
service->port = 0;
service->ssl_enabled = 0;
- ldret = lockdownd_start_service(client, "com.apple.afc", &service);
+ ldret = lockdownd_start_service(client, AFC_SERVICE_NAME, &service);
if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
afc_client_new(device, service, &afc);
+ } else {
+ printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
}
}
@@ -853,7 +885,7 @@ int main(int argc, char *argv[])
ldret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &service);
if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, service->port);
- mobilebackup_client_new(device, service, &mobilebackup);
+ printf("%d\n", mobilebackup_client_new(device, service, &mobilebackup));
if (service) {
lockdownd_service_descriptor_free(service);
@@ -869,7 +901,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");
@@ -880,7 +912,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;
@@ -913,15 +945,16 @@ int main(int argc, char *argv[])
if (aerr == AFC_E_SUCCESS) {
do_post_notification(NP_SYNC_DID_START);
break;
- } else if (aerr == AFC_E_OP_WOULD_BLOCK) {
+ }
+ if (aerr == AFC_E_OP_WOULD_BLOCK) {
usleep(LOCK_WAIT);
continue;
- } else {
- fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
- afc_file_close(afc, lockfile);
- lockfile = 0;
- cmd = CMD_LEAVE;
}
+
+ fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
+ afc_file_close(afc, lockfile);
+ lockfile = 0;
+ cmd = CMD_LEAVE;
}
if (i == LOCK_ATTEMPTS) {
fprintf(stderr, "ERROR: timeout while locking for sync\n");
@@ -945,7 +978,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;
@@ -963,7 +996,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);
@@ -995,7 +1028,7 @@ int main(int argc, char *argv[])
} else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) {
printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
} else {
- printf("ERROR: Could not start backup process: unspecified error occurred\n");
+ printf("ERROR: Could not start backup process: unspecified error occurred (%d)\n", err);
}
break;
}
@@ -1017,6 +1050,7 @@ int main(int argc, char *argv[])
char *format_size = NULL;
int is_manifest = 0;
uint8_t b = 0;
+ uint64_t u64val = 0;
/* process series of DLSendFile messages */
do {
@@ -1048,8 +1082,8 @@ int main(int argc, char *argv[])
/* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */
node = plist_dict_get_item(node_tmp, "DLFileStatusKey");
- plist_get_uint_val(node, &c);
- file_status = c;
+ plist_get_uint_val(node, &u64val);
+ file_status = u64val;
/* get source filename */
node = plist_dict_get_item(node_tmp, "BackupManifestKey");
@@ -1101,7 +1135,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);
}
@@ -1143,9 +1177,8 @@ int main(int argc, char *argv[])
if ((!is_manifest)) {
if (hunk_index == 0 && file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
print_progress(100);
- } else {
- if (file_size > 0)
- print_progress((double)((file_size_current*100)/file_size));
+ } else if (file_size > 0) {
+ print_progress((double)(file_size_current*100)/file_size);
}
}
@@ -1214,7 +1247,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;
@@ -1245,7 +1278,7 @@ 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;
@@ -1372,7 +1405,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");
@@ -1592,7 +1625,7 @@ files_out:
if (manifest_path)
free(manifest_path);
} else {
- printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME);
+ printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP_SERVICE_NAME, lockdownd_strerror(ldret));
lockdownd_client_free(client);
client = NULL;
}
@@ -1613,9 +1646,7 @@ files_out:
idevice_free(device);
- if (udid) {
- free(udid);
- }
+ free(udid);
return 0;
}
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
index c2f04c2..c73b269 100644
--- a/tools/idevicebackup2.c
+++ b/tools/idevicebackup2.c
@@ -2,7 +2,7 @@
* idevicebackup2.c
* Command line interface to use the device's backup and restore service
*
- * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2010-2022 Nikias Bassen, All Rights Reserved.
* Copyright (c) 2009-2010 Martin Szulecki, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
@@ -24,6 +24,8 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicebackup2"
+
#include <stdio.h>
#include <string.h>
#include <errno.h>
@@ -34,6 +36,7 @@
#include <libgen.h>
#include <ctype.h>
#include <time.h>
+#include <getopt.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
@@ -43,7 +46,8 @@
#include <libimobiledevice/installation_proxy.h>
#include <libimobiledevice/sbservices.h>
#include <libimobiledevice/diagnostics_relay.h>
-#include "common/utils.h"
+#include <libimobiledevice-glue/utils.h>
+#include <plist/plist.h>
#include <endianness.h>
@@ -54,6 +58,9 @@
#include <windows.h>
#include <conio.h>
#define sleep(x) Sleep(x*1000)
+#ifndef ELOOP
+#define ELOOP 114
+#endif
#else
#include <termios.h>
#include <sys/statvfs.h>
@@ -178,9 +185,8 @@ static int mkdir_with_parents(const char *dir, int mode)
if (!dir) return -1;
if (__mkdir(dir, mode) == 0) {
return 0;
- } else {
- if (errno == EEXIST) return 0;
}
+ if (errno == EEXIST) return 0;
int res;
char *parent = strdup(dir);
char *parentdir = dirname(parent);
@@ -346,7 +352,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
char *udid_uppercase = NULL;
lockdownd_client_t lockdown = NULL;
- if (lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2") != LOCKDOWN_E_SUCCESS) {
+ if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
return NULL;
}
@@ -367,7 +373,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
plist_t app_dict = plist_new_dict();
plist_t installed_apps = plist_new_array();
instproxy_client_t ip = NULL;
- if (instproxy_client_start_service(device, &ip, "idevicebackup2") == INSTPROXY_E_SUCCESS) {
+ if (instproxy_client_start_service(device, &ip, TOOL_NAME) == INSTPROXY_E_SUCCESS) {
plist_t client_opts = instproxy_client_options_new();
instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata", NULL);
@@ -376,7 +382,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
instproxy_browse(ip, client_opts, &apps);
sbservices_client_t sbs = NULL;
- if (sbservices_client_start_service(device, &sbs, "idevicebackup2") != SBSERVICES_E_SUCCESS) {
+ if (sbservices_client_start_service(device, &sbs, TOOL_NAME) != SBSERVICES_E_SUCCESS) {
printf("Couldn't establish sbservices connection. Continuing anyway.\n");
}
@@ -546,9 +552,11 @@ static int write_restore_applications(plist_t info_plist, afc_client_t afc)
uint32_t applications_plist_xml_length = 0;
plist_t applications_plist = plist_dict_get_item(info_plist, "Applications");
- if (applications_plist) {
- plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
+ if (!applications_plist) {
+ printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n");
+ return 0;
}
+ plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
if (!applications_plist_xml) {
printf("Error preparing RestoreApplications.plist\n");
goto leave;
@@ -599,7 +607,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");
@@ -627,19 +635,19 @@ static void do_post_notification(idevice_t device, const char *notification)
lockdownd_client_t lockdown = NULL;
- if (lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2") != LOCKDOWN_E_SUCCESS) {
+ if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
return;
}
- lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
- if (service && service->port) {
+ lockdownd_error_t ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
+ if (ldret == LOCKDOWN_E_SUCCESS) {
np_client_new(device, service, &np);
if (np) {
np_post_notification(np, notification);
np_client_free(np);
}
} else {
- printf("Could not start %s\n", NP_SERVICE_NAME);
+ printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
}
if (service) {
@@ -738,8 +746,18 @@ static int errno_to_device_error(int errno_value)
return -6;
case EEXIST:
return -7;
+ case ENOTDIR:
+ return -8;
+ case EISDIR:
+ return -9;
+ case ELOOP:
+ return -10;
+ case EIO:
+ return -11;
+ case ENOSPC:
+ return -15;
default:
- return -errno_value;
+ return -1;
}
}
@@ -955,10 +973,12 @@ static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** fil
if ((nlen == 0) && (rlen == 4)) {
// a zero length means no more files to receive
return 0;
- } else if(rlen == 0) {
+ }
+ if (rlen == 0) {
// device needs more time, waiting...
continue;
- } else if (nlen > 4096) {
+ }
+ if (nlen > 4096) {
// filename length is too large
printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
return 0;
@@ -1350,7 +1370,8 @@ static void get_hidden_input(char *buf, int maxlen)
while ((c = my_getch())) {
if ((c == '\r') || (c == '\n')) {
break;
- } else if (isprint(c)) {
+ }
+ if (isprint(c)) {
if (pwlen < maxlen-1)
buf[pwlen++] = c;
fputc('*', stderr);
@@ -1398,51 +1419,60 @@ static void clean_exit(int sig)
quit_flag++;
}
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
- printf("Create or restore backup from the current or specified directory.\n\n");
- printf("commands:\n");
- printf(" backup\tcreate backup for the device\n");
- printf(" --full\t\tforce full backup from device.\n");
- printf(" restore\trestore last backup to the device\n");
- printf(" --system\t\trestore system files, too.\n");
- printf(" --no-reboot\t\tdo NOT reboot the device when done (default: yes).\n");
- printf(" --copy\t\tcreate a copy of backup folder before restoring.\n");
- printf(" --settings\t\trestore device settings from the backup.\n");
- printf(" --remove\t\tremove items which are not being restored\n");
- printf(" --skip-apps\t\tdo not trigger re-installation of apps after restore\n");
- printf(" --password PWD\tsupply the password of the source backup\n");
- printf(" info\t\tshow details about last completed backup of device\n");
- printf(" list\t\tlist files of last completed backup in CSV format\n");
- printf(" unback\tunpack a completed backup in DIRECTORY/_unback_/\n");
- printf(" encryption on|off [PWD]\tenable or disable backup encryption\n");
- printf(" NOTE: password will be requested in interactive mode if omitted\n");
- printf(" changepw [OLD NEW] change backup password on target device\n");
- printf(" NOTE: passwords will be requested in interactive mode if omitted\n");
- printf(" cloud on|off\tenable or disable cloud use (requires iCloud account)\n");
- printf("\n");
- printf("options:\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -s, --source UDID\tuse backup data from device specified by UDID\n");
- printf(" -i, --interactive\trequest passwords interactively\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Create or restore backup in/from the specified directory.\n"
+ "\n"
+ "CMD:\n"
+ " backup create backup for the device\n"
+ " --full force full backup from device.\n"
+ " restore restore last backup to the device\n"
+ " --system restore system files, too.\n"
+ " --no-reboot do NOT reboot the device when done (default: yes).\n"
+ " --copy create a copy of backup folder before restoring.\n"
+ " --settings restore device settings from the backup.\n"
+ " --remove remove items which are not being restored\n"
+ " --skip-apps do not trigger re-installation of apps after restore\n"
+ " --password PWD supply the password for the encrypted source backup\n"
+ " info show details about last completed backup of device\n"
+ " list list files of last completed backup in CSV format\n"
+ " unback unpack a completed backup in DIRECTORY/_unback_/\n"
+ " encryption on|off [PWD] enable or disable backup encryption\n"
+ " changepw [OLD NEW] change backup password on target device\n"
+ " cloud on|off enable or disable cloud use (requires iCloud account)\n"
+ "\n"
+ "NOTE: Passwords will be requested in interactive mode (-i) if omitted, or can\n"
+ "be passed via environment variable BACKUP_PASSWORD/BACKUP_PASSWORD_NEW.\n"
+ "See man page for further details.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -s, --source UDID use backup data from device specified by UDID\n"
+ " -n, --network connect to network device\n"
+ " -i, --interactive request passwords interactively\n"
+ " -d, --debug enable communication debugging\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
-#define DEVICE_VERSION(maj, min, patch) (((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF))
+#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;
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
- int i;
+ int i = 0;
char* udid = NULL;
char* source_udid = NULL;
+ int use_network = 0;
lockdownd_service_descriptor_t service = NULL;
int cmd = -1;
int cmd_flags = 0;
@@ -1456,7 +1486,46 @@ int main(int argc, char *argv[])
plist_t node_tmp = NULL;
plist_t info_plist = NULL;
plist_t opts = NULL;
+
+ idevice_t device = NULL;
+ afc_client_t afc = NULL;
+ np_client_t np = NULL;
+ lockdownd_client_t lockdown = NULL;
+ mobilebackup2_client_t mobilebackup2 = NULL;
mobilebackup2_error_t err;
+ uint64_t lockfile = 0;
+
+#define OPT_SYSTEM 1
+#define OPT_REBOOT 2
+#define OPT_NO_REBOOT 3
+#define OPT_COPY 4
+#define OPT_SETTINGS 5
+#define OPT_REMOVE 6
+#define OPT_SKIP_APPS 7
+#define OPT_PASSWORD 8
+#define OPT_FULL 9
+
+ int c = 0;
+ const struct option longopts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+ { "source", required_argument, NULL, 's' },
+ { "interactive", no_argument, NULL, 'i' },
+ { "network", no_argument, NULL, 'n' },
+ { "version", no_argument, NULL, 'v' },
+ // command options:
+ { "system", no_argument, NULL, OPT_SYSTEM },
+ { "reboot", no_argument, NULL, OPT_REBOOT },
+ { "no-reboot", no_argument, NULL, OPT_NO_REBOOT },
+ { "copy", no_argument, NULL, OPT_COPY },
+ { "settings", no_argument, NULL, OPT_SETTINGS },
+ { "remove", no_argument, NULL, OPT_REMOVE },
+ { "skip-apps", no_argument, NULL, OPT_SKIP_APPS },
+ { "password", required_argument, NULL, OPT_PASSWORD },
+ { "full", no_argument, NULL, OPT_FULL },
+ { NULL, 0, NULL, 0}
+ };
/* we need to exit cleanly on running backups and restores or we cause havok */
signal(SIGINT, clean_exit);
@@ -1467,212 +1536,207 @@ int main(int argc, char *argv[])
#endif
/* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:s:inv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return -1;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = strdup(argv[i]);
- continue;
- }
- else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--source")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return -1;
+ udid = strdup(optarg);
+ break;
+ case 's':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: SOURCE argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- source_udid = strdup(argv[i]);
- continue;
- }
- else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--interactive")) {
+ source_udid = strdup(optarg);
+ break;
+ case 'i':
interactive_mode = 1;
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'h':
+ print_usage(argc, argv, 0);
return 0;
- }
- else if (!strcmp(argv[i], "backup")) {
- cmd = CMD_BACKUP;
- }
- else if (!strcmp(argv[i], "restore")) {
- cmd = CMD_RESTORE;
- }
- else if (!strcmp(argv[i], "--system")) {
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ return 0;
+ case OPT_SYSTEM:
cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES;
- }
- else if (!strcmp(argv[i], "--reboot")) {
+ break;
+ case OPT_REBOOT:
cmd_flags &= ~CMD_FLAG_RESTORE_NO_REBOOT;
- }
- else if (!strcmp(argv[i], "--no-reboot")) {
+ break;
+ case OPT_NO_REBOOT:
cmd_flags |= CMD_FLAG_RESTORE_NO_REBOOT;
- }
- else if (!strcmp(argv[i], "--copy")) {
+ break;
+ case OPT_COPY:
cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP;
- }
- else if (!strcmp(argv[i], "--settings")) {
+ break;
+ case OPT_SETTINGS:
cmd_flags |= CMD_FLAG_RESTORE_SETTINGS;
- }
- else if (!strcmp(argv[i], "--remove")) {
+ break;
+ case OPT_REMOVE:
cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
- }
- else if (!strcmp(argv[i], "--skip-apps")) {
+ break;
+ case OPT_SKIP_APPS:
cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS;
- }
- else if (!strcmp(argv[i], "--password")) {
- i++;
- if (!argv[i]) {
- print_usage(argc, argv);
- return -1;
- }
- if (backup_password)
- free(backup_password);
- backup_password = strdup(argv[i]);
- continue;
- }
- else if (!strcmp(argv[i], "cloud")) {
- cmd = CMD_CLOUD;
- i++;
- if (!argv[i]) {
- printf("No argument given for cloud command; requires either 'on' or 'off'.\n");
- print_usage(argc, argv);
- return -1;
- }
- if (!strcmp(argv[i], "on")) {
- cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
- } else if (!strcmp(argv[i], "off")) {
- cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
- } else {
- printf("Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]);
- }
- continue;
- }
- else if (!strcmp(argv[i], "--full")) {
+ break;
+ case OPT_PASSWORD:
+ free(backup_password);
+ backup_password = strdup(optarg);
+ break;
+ case OPT_FULL:
cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP;
+ break;
+ default:
+ print_usage(argc, argv, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "info")) {
- cmd = CMD_INFO;
- verbose = 0;
- }
- else if (!strcmp(argv[i], "list")) {
- cmd = CMD_LIST;
- verbose = 0;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!argv[0]) {
+ fprintf(stderr, "ERROR: No command specified.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+
+ if (!strcmp(argv[0], "backup")) {
+ cmd = CMD_BACKUP;
+ }
+ else if (!strcmp(argv[0], "restore")) {
+ cmd = CMD_RESTORE;
+ }
+ else if (!strcmp(argv[0], "cloud")) {
+ cmd = CMD_CLOUD;
+ i = 1;
+ if (!argv[i]) {
+ fprintf(stderr, "ERROR: No argument given for cloud command; requires either 'on' or 'off'.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "unback")) {
- cmd = CMD_UNBACK;
+ if (!strcmp(argv[i], "on")) {
+ cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
+ } else if (!strcmp(argv[i], "off")) {
+ cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
+ } else {
+ fprintf(stderr, "ERROR: Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+ }
+ else if (!strcmp(argv[0], "info")) {
+ cmd = CMD_INFO;
+ verbose = 0;
+ }
+ else if (!strcmp(argv[0], "list")) {
+ cmd = CMD_LIST;
+ verbose = 0;
+ }
+ else if (!strcmp(argv[0], "unback")) {
+ cmd = CMD_UNBACK;
+ }
+ else if (!strcmp(argv[0], "encryption")) {
+ cmd = CMD_CHANGEPW;
+ i = 1;
+ if (!argv[i]) {
+ fprintf(stderr, "ERROR: No argument given for encryption command; requires either 'on' or 'off'.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+ if (!strcmp(argv[i], "on")) {
+ cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
+ } else if (!strcmp(argv[i], "off")) {
+ cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
+ } else {
+ fprintf(stderr, "ERROR: Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "encryption")) {
- cmd = CMD_CHANGEPW;
- i++;
- if (!argv[i]) {
- printf("No argument given for encryption command; requires either 'on' or 'off'.\n");
- print_usage(argc, argv);
- return -1;
- }
- if (!strcmp(argv[i], "on")) {
- cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
- } else if (!strcmp(argv[i], "off")) {
- cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
- } else {
- printf("Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
- }
- // check if a password was given on the command line
- if (newpw) {
- free(newpw);
- newpw = NULL;
- }
- if (backup_password) {
- free(backup_password);
- backup_password = NULL;
- }
- i++;
- if (argv[i]) {
- if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
- newpw = strdup(argv[i]);
- } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
- backup_password = strdup(argv[i]);
- }
+ // check if a password was given on the command line
+ free(newpw);
+ newpw = NULL;
+ free(backup_password);
+ backup_password = NULL;
+ i++;
+ if (argv[i]) {
+ if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
+ newpw = strdup(argv[i]);
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
+ backup_password = strdup(argv[i]);
}
- continue;
}
- else if (!strcmp(argv[i], "changepw")) {
- cmd = CMD_CHANGEPW;
- cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
- // check if passwords were given on command line
- if (newpw) {
- free(newpw);
- newpw = NULL;
- }
- if (backup_password) {
- free(backup_password);
- backup_password = NULL;
- }
+ }
+ else if (!strcmp(argv[0], "changepw")) {
+ cmd = CMD_CHANGEPW;
+ cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
+ // check if passwords were given on command line
+ free(newpw);
+ newpw = NULL;
+ free(backup_password);
+ backup_password = NULL;
+ i = 1;
+ if (argv[i]) {
+ backup_password = strdup(argv[i]);
i++;
- if (argv[i]) {
- backup_password = strdup(argv[i]);
- i++;
- if (!argv[i]) {
- printf("Old and new passwords have to be passed as arguments for the changepw command\n");
- print_usage(argc, argv);
- return -1;
- }
- newpw = strdup(argv[i]);
+ if (!argv[i]) {
+ fprintf(stderr, "ERROR: Old and new passwords have to be passed as arguments for the changepw command\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- continue;
- }
- else if (backup_directory == NULL) {
- backup_directory = argv[i];
- }
- else {
- print_usage(argc, argv);
- return -1;
+ newpw = strdup(argv[i]);
}
}
+ i++;
+ if (argv[i]) {
+ backup_directory = argv[i];
+ }
+
/* verify options */
if (cmd == -1) {
- printf("No command specified.\n");
- print_usage(argc, argv);
- return -1;
+ fprintf(stderr, "ERROR: Unsupported command '%s'.\n", argv[0]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
if (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {
backup_directory = (char*)".this_folder_is_not_present_on_purpose";
} else {
if (backup_directory == NULL) {
- printf("No target backup directory specified.\n");
- print_usage(argc, argv);
- return -1;
+ fprintf(stderr, "ERROR: No target backup directory specified.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
/* verify if passed backup directory exists */
if (stat(backup_directory, &st) != 0) {
- printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
+ fprintf(stderr, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
return -1;
}
}
- idevice_t device = NULL;
- if (udid) {
- ret = idevice_new(&device, udid);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found with udid %s, is it plugged in?\n", udid);
- return -1;
+ 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;
}
- else
- {
- ret = idevice_new(&device, NULL);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found, is it plugged in?\n");
- return -1;
- }
+
+ if (!udid) {
idevice_get_udid(device, &udid);
}
@@ -1683,6 +1747,20 @@ int main(int argc, char *argv[])
uint8_t is_encrypted = 0;
char *info_path = NULL;
if (cmd == CMD_CHANGEPW) {
+ if (!interactive_mode) {
+ if (!newpw) {
+ newpw = getenv("BACKUP_PASSWORD_NEW");
+ if (newpw) {
+ newpw = strdup(newpw);
+ }
+ }
+ if (!backup_password) {
+ backup_password = getenv("BACKUP_PASSWORD");
+ if (backup_password) {
+ backup_password = strdup(backup_password);
+ }
+ }
+ }
if (!interactive_mode && !backup_password && !newpw) {
idevice_free(device);
printf("ERROR: Can't get password input in non-interactive mode. Either pass password(s) on the command line, or enable interactive mode with -i or --interactive.\n");
@@ -1703,7 +1781,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);
@@ -1724,6 +1802,12 @@ int main(int argc, char *argv[])
if (cmd != CMD_CLOUD && is_encrypted) {
PRINT_VERBOSE(1, "This is an encrypted backup.\n");
if (backup_password == NULL) {
+ backup_password = getenv("BACKUP_PASSWORD");
+ if (backup_password) {
+ backup_password = strdup(backup_password);
+ }
+ }
+ if (backup_password == NULL) {
if (interactive_mode) {
backup_password = ask_for_password("Enter backup password", 0);
}
@@ -1742,8 +1826,7 @@ int main(int argc, char *argv[])
}
}
- lockdownd_client_t lockdown = NULL;
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2"))) {
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
idevice_free(device);
return -1;
@@ -1780,7 +1863,6 @@ int main(int argc, char *argv[])
}
/* start notification_proxy */
- np_client_t np = NULL;
ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
np_client_new(device, service, &np);
@@ -1794,17 +1876,24 @@ int main(int argc, char *argv[])
};
np_observe_notifications(np, noties);
} else {
- printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
+ printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
+ cmd = CMD_LEAVE;
+ goto checkpoint;
+ }
+ if (service) {
+ lockdownd_service_descriptor_free(service);
+ service = NULL;
}
- afc_client_t afc = NULL;
if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
/* start AFC, we need this for the lock file */
- service->port = 0;
- service->ssl_enabled = 0;
ldret = lockdownd_start_service(lockdown, AFC_SERVICE_NAME, &service);
if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
afc_client_new(device, service, &afc);
+ } else {
+ printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
+ cmd = CMD_LEAVE;
+ goto checkpoint;
}
}
@@ -1814,7 +1903,6 @@ int main(int argc, char *argv[])
}
/* start mobilebackup service and retrieve port */
- mobilebackup2_client_t mobilebackup2 = NULL;
ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
lockdownd_client_free(lockdown);
lockdown = NULL;
@@ -1849,7 +1937,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");
@@ -1864,7 +1952,6 @@ int main(int argc, char *argv[])
}
}
- uint64_t lockfile = 0;
if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
do_post_notification(device, NP_SYNC_WILL_START);
afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
@@ -1877,15 +1964,16 @@ int main(int argc, char *argv[])
if (aerr == AFC_E_SUCCESS) {
do_post_notification(device, NP_SYNC_DID_START);
break;
- } else if (aerr == AFC_E_OP_WOULD_BLOCK) {
+ }
+ if (aerr == AFC_E_OP_WOULD_BLOCK) {
usleep(LOCK_WAIT);
continue;
- } else {
- fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
- afc_file_close(afc, lockfile);
- lockfile = 0;
- cmd = CMD_LEAVE;
}
+
+ fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
+ afc_file_close(afc, lockfile);
+ lockfile = 0;
+ cmd = CMD_LEAVE;
}
if (i == LOCK_ATTEMPTS) {
fprintf(stderr, "ERROR: timeout while locking for sync\n");
@@ -1943,7 +2031,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);
@@ -2004,9 +2092,8 @@ checkpoint:
PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));
plist_dict_set_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
- if (cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS)
- plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(1));
- PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
+ plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS));
+ PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
if (backup_password != NULL) {
plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
}
@@ -2020,9 +2107,8 @@ checkpoint:
if (write_restore_applications(info_plist, afc) < 0) {
cmd = CMD_LEAVE;
break;
- } else {
- PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
}
+ PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
}
/* Start restore */
@@ -2077,6 +2163,12 @@ checkpoint:
if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
if (!willEncrypt) {
if (!newpw) {
+ newpw = getenv("BACKUP_PASSWORD");
+ if (newpw) {
+ newpw = strdup(newpw);
+ }
+ }
+ if (!newpw) {
newpw = ask_for_password("Enter new backup password", 1);
}
if (!newpw) {
@@ -2093,6 +2185,12 @@ checkpoint:
} else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
if (willEncrypt) {
if (!backup_password) {
+ backup_password = getenv("BACKUP_PASSWORD");
+ if (backup_password) {
+ backup_password = strdup(backup_password);
+ }
+ }
+ if (!backup_password) {
backup_password = ask_for_password("Enter current backup password", 0);
}
} else {
@@ -2133,7 +2231,7 @@ checkpoint:
uint8_t passcode_hint = 0;
if (device_version >= DEVICE_VERSION(13,0,0)) {
diagnostics_relay_client_t diag = NULL;
- if (diagnostics_relay_client_start_service(device, &diag, "idevicebackup2") == DIAGNOSTICS_RELAY_E_SUCCESS) {
+ if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
plist_t dict = NULL;
plist_t keys = plist_new_array();
plist_array_append_item(keys, plist_new_string("PasswordConfigured"));
@@ -2224,6 +2322,11 @@ checkpoint:
plist_t freespace_item = plist_new_uint(freespace);
mobilebackup2_send_status_response(mobilebackup2, res, NULL, freespace_item);
plist_free(freespace_item);
+ } else if (!strcmp(dlmsg, "DLMessagePurgeDiskSpace")) {
+ /* device wants to purge disk space on the host - not supported */
+ plist_t empty_dict = plist_new_dict();
+ err = mobilebackup2_send_status_response(mobilebackup2, -1, "Operation not supported", empty_dict);
+ plist_free(empty_dict);
} else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
/* list directory contents */
mb2_handle_list_directory(mobilebackup2, message, backup_directory);
@@ -2411,7 +2514,7 @@ checkpoint:
/* print status */
if ((overall_progress > 0) && !progress_finished) {
- if (overall_progress >= 100.0f) {
+ if (overall_progress >= 100.0F) {
progress_finished = 1;
}
print_progress_real(overall_progress, 0);
@@ -2535,7 +2638,7 @@ files_out:
do_post_notification(device, NP_SYNC_DID_FINISH);
}
} else {
- printf("ERROR: Could not start service %s.\n", MOBILEBACKUP2_SERVICE_NAME);
+ printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP2_SERVICE_NAME, lockdownd_strerror(ldret));
lockdownd_client_free(lockdown);
lockdown = NULL;
}
diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c
new file mode 100644
index 0000000..8de6b22
--- /dev/null
+++ b/tools/idevicebtlogger.c
@@ -0,0 +1,458 @@
+/*
+ * idevicebt_packet_logger.c
+ * Capture Bluetooth HCI traffic to native PKLG or PCAP
+ *
+ * Copyright (c) 2021 Geoffrey Kruse, All Rights Reserved.
+ * Copyright (c) 2022 Matthias Ringwald, 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 "idevicebtlogger"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <assert.h>
+#include <fcntl.h>
+
+#ifdef WIN32
+#include <windows.h>
+#define sleep(x) Sleep(x*1000)
+#else
+#include <arpa/inet.h>
+#endif
+
+
+#include <libimobiledevice/libimobiledevice.h>
+#include <libimobiledevice/bt_packet_logger.h>
+
+typedef enum {
+ HCI_COMMAND = 0x00,
+ HCI_EVENT = 0x01,
+ SENT_ACL_DATA = 0x02,
+ RECV_ACL_DATA = 0x03,
+ SENT_SCO_DATA = 0x08,
+ RECV_SCO_DATA = 0x09,
+} PacketLoggerPacketType;
+
+static int quit_flag = 0;
+static int exit_on_disconnect = 0;
+
+static char* udid = NULL;
+static idevice_t device = NULL;
+static bt_packet_logger_client_t bt_packet_logger = NULL;
+static int use_network = 0;
+static char* out_filename = NULL;
+static char* log_format_string = NULL;
+static FILE * packetlogger_file = NULL;
+
+static enum {
+ LOG_FORMAT_PACKETLOGGER,
+ LOG_FORMAT_PCAP
+} log_format = LOG_FORMAT_PACKETLOGGER;
+
+const uint8_t pcap_file_header[] = {
+ // Magic Number
+ 0xA1, 0xB2, 0xC3, 0xD4,
+ // Major / Minor Version
+ 0x00, 0x02, 0x00, 0x04,
+ // Reserved1
+ 0x00, 0x00, 0x00, 0x00,
+ // Reserved2
+ 0x00, 0x00, 0x00, 0x00,
+ // Snaplen == max packet size - use 2kB (larger than any ACL)
+ 0x00, 0x00, 0x08, 0x00,
+ // LinkType: DLT_BLUETOOTH_HCI_H4_WITH_PHDR
+ 0x00, 0x00, 0x00, 201,
+};
+
+static uint32_t big_endian_read_32(const uint8_t * buffer, int position)
+{
+ return ((uint32_t) buffer[position+3]) | (((uint32_t)buffer[position+2]) << 8) | (((uint32_t)buffer[position+1]) << 16) | (((uint32_t) buffer[position]) << 24);
+}
+
+static void big_endian_store_32(uint8_t * buffer, uint16_t position, uint32_t value)
+{
+ uint16_t pos = position;
+ buffer[pos++] = (uint8_t)(value >> 24);
+ buffer[pos++] = (uint8_t)(value >> 16);
+ buffer[pos++] = (uint8_t)(value >> 8);
+ buffer[pos++] = (uint8_t)(value);
+}
+
+/**
+ * Callback from the packet logger service to handle packets and log to PacketLogger format
+ */
+static void bt_packet_logger_callback_packetlogger(uint8_t * data, uint16_t len, void *user_data)
+{
+ (void) fwrite(data, 1, len, packetlogger_file);
+}
+
+/**
+ * Callback from the packet logger service to handle packets and log to pcap
+ */
+static void bt_packet_logger_callback_pcap(uint8_t * data, uint16_t len, void *user_data)
+{
+ // check len
+ if (len < 13) {
+ return;
+ }
+
+ // parse packet header (ignore len field)
+ uint32_t ts_secs = big_endian_read_32(data, 4);
+ uint32_t ts_us = big_endian_read_32(data, 8);
+ uint8_t packet_type = data[12];
+ data += 13;
+ len -= 13;
+
+ // map PacketLogger packet type onto PCAP direction flag and hci_h4_type
+ uint8_t direction_in = 0;
+ uint8_t hci_h4_type = 0xff;
+ switch(packet_type) {
+ case HCI_COMMAND:
+ hci_h4_type = 0x01;
+ direction_in = 0;
+ break;
+ case SENT_ACL_DATA:
+ hci_h4_type = 0x02;
+ direction_in = 0;
+ break;
+ case RECV_ACL_DATA:
+ hci_h4_type = 0x02;
+ direction_in = 1;
+ break;
+ case SENT_SCO_DATA:
+ hci_h4_type = 0x03;
+ direction_in = 0;
+ break;
+ case RECV_SCO_DATA:
+ hci_h4_type = 0x03;
+ direction_in = 1;
+ break;
+ case HCI_EVENT:
+ hci_h4_type = 0x04;
+ direction_in = 1;
+ break;
+ default:
+ // unknown packet logger type, drop packet
+ return;
+ }
+
+ // setup pcap record header, 4 byte direction flag, 1 byte HCI H4 packet type, data
+ uint8_t pcap_record_header[21];
+ big_endian_store_32(pcap_record_header, 0, ts_secs); // Timestamp seconds
+ big_endian_store_32(pcap_record_header, 4, ts_us); // Timestamp microseconds
+ big_endian_store_32(pcap_record_header, 8, 4 + 1 + len); // Captured Packet Length
+ big_endian_store_32(pcap_record_header, 12, 4 + 1 + len); // Original Packet Length
+ big_endian_store_32(pcap_record_header, 16, direction_in); // Direction: Incoming = 1
+ pcap_record_header[20] = hci_h4_type;
+
+ // write header
+ (void) fwrite(pcap_record_header, 1, sizeof(pcap_record_header), packetlogger_file);
+
+ // write packet
+ (void) fwrite(data, 1, len, packetlogger_file);
+
+ // flush
+ (void) fflush(packetlogger_file);
+}
+
+/**
+ * Disable HCI log capture
+ */
+static void stop_logging(void)
+{
+ fflush(NULL);
+
+ if (bt_packet_logger) {
+ bt_packet_logger_client_free(bt_packet_logger);
+ bt_packet_logger = NULL;
+ }
+
+ if (device) {
+ idevice_free(device);
+ device = NULL;
+ }
+}
+
+/**
+ * Enable HCI log capture
+ */
+static int start_logging(void)
+{
+ idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
+ if (ret != IDEVICE_E_SUCCESS) {
+ fprintf(stderr, "Device with udid %s not found!?\n", udid);
+ return -1;
+ }
+
+ lockdownd_client_t lockdown = NULL;
+ lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr);
+ idevice_free(device);
+ device = NULL;
+ return -1;
+ }
+
+ /* start bt_packet_logger service */
+ bt_packet_logger_client_start_service(device, &bt_packet_logger, TOOL_NAME);
+
+ /* start capturing bt_packet_logger */
+ void (*callback)(uint8_t * data, uint16_t len, void *user_data);
+ switch (log_format){
+ case LOG_FORMAT_PCAP:
+ callback = bt_packet_logger_callback_pcap;
+ break;
+ case LOG_FORMAT_PACKETLOGGER:
+ callback = bt_packet_logger_callback_packetlogger;
+ break;
+ default:
+ assert(0);
+ return 0;
+ }
+ bt_packet_logger_error_t serr = bt_packet_logger_start_capture(bt_packet_logger, callback, NULL);
+ if (serr != BT_PACKET_LOGGER_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Unable to start capturing bt_packet_logger.\n");
+ bt_packet_logger_client_free(bt_packet_logger);
+ bt_packet_logger = NULL;
+ idevice_free(device);
+ device = NULL;
+ return -1;
+ }
+
+ fprintf(stderr, "[connected:%s]\n", udid);
+ fflush(stderr);
+
+ return 0;
+}
+
+/**
+ * Callback for device events
+ */
+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 (!bt_packet_logger) {
+ if (!udid) {
+ udid = strdup(event->udid);
+ }
+ if (strcmp(udid, event->udid) == 0) {
+ if (start_logging() != 0) {
+ fprintf(stderr, "Could not start logger for udid %s\n", udid);
+ }
+ }
+ }
+ } else if (event->event == IDEVICE_DEVICE_REMOVE) {
+ if (bt_packet_logger && (strcmp(udid, event->udid) == 0)) {
+ stop_logging();
+ fprintf(stderr, "[disconnected:%s]\n", udid);
+ if (exit_on_disconnect) {
+ quit_flag++;
+ }
+ }
+ }
+}
+
+/**
+ * signal handler function for cleaning up properly
+ */
+static void clean_exit(int sig)
+{
+ fprintf(stderr, "\nExiting...\n");
+ quit_flag++;
+}
+
+/**
+ * print usage information
+ */
+static void print_usage(int argc, char **argv, int is_error)
+{
+ char *name = NULL;
+ name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] <FILE>\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n" \
+ "Capture HCI packets from a connected device.\n" \
+ "\n" \
+ "OPTIONS:\n" \
+ " -u, --udid UDID target specific device by UDID\n" \
+ " -n, --network connect to network device\n" \
+ " -f, --format FORMAT logging format: packetlogger (default) or pcap\n" \
+ " -x, --exit exit when device disconnects\n" \
+ " -h, --help prints usage information\n" \
+ " -d, --debug enable communication debugging\n" \
+ " -v, --version prints version information\n" \
+ "\n" \
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
+}
+
+/**
+ * Program entry
+ */
+int main(int argc, char *argv[])
+{
+ int c = 0;
+ const struct option longopts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+ { "format", required_argument, NULL, 'f' },
+ { "network", no_argument, NULL, 'n' },
+ { "exit", no_argument, NULL, 'x' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
+ };
+
+ signal(SIGINT, clean_exit);
+ signal(SIGTERM, clean_exit);
+#ifndef WIN32
+ signal(SIGQUIT, clean_exit);
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ while ((c = getopt_long(argc, argv, "dhu:f:nxv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
+ idevice_set_debug_level(1);
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ free(udid);
+ udid = strdup(optarg);
+ break;
+ case 'f':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: FORMAT must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ free(log_format_string);
+ log_format_string = strdup(optarg);
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'x':
+ exit_on_disconnect = 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;
+ }
+ }
+
+ if (optind < argc) {
+ out_filename = argv[optind];
+ // printf("Output File: %s\n", out_filename);
+ }
+ else {
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+
+ if (log_format_string != NULL){
+ if (strcmp("packetlogger", log_format_string) == 0){
+ log_format = LOG_FORMAT_PACKETLOGGER;
+ } else if (strcmp("pcap", log_format_string) == 0){
+ log_format = LOG_FORMAT_PCAP;
+ } else {
+ printf("Unknown logging format: '%s'\n", log_format_string);
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ }
+
+ int num = 0;
+ idevice_info_t *devices = NULL;
+ idevice_get_device_list_extended(&devices, &num);
+ idevice_device_list_extended_free(devices);
+ if (num == 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;
+ } else {
+ fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
+ }
+ }
+
+ // support streaming to stdout
+ if (strcmp(out_filename, "-") == 0){
+ packetlogger_file = stdout;
+ } else {
+ packetlogger_file = fopen(out_filename, "wb");
+ }
+
+
+ if (packetlogger_file == NULL){
+ fprintf(stderr, "Failed to open file %s, errno = %d\n", out_filename, errno);
+ return -2;
+ }
+
+ switch (log_format){
+ case LOG_FORMAT_PCAP:
+ // printf("Output Format: PCAP\n");
+ // write PCAP file header
+ (void) fwrite(&pcap_file_header, 1, sizeof(pcap_file_header), packetlogger_file);
+ break;
+ case LOG_FORMAT_PACKETLOGGER:
+ printf("Output Format: PacketLogger\n");
+ break;
+ default:
+ assert(0);
+ return -2;
+ }
+ idevice_subscription_context_t context = NULL;
+ idevice_events_subscribe(&context, device_event_cb, NULL);
+
+ while (!quit_flag) {
+ sleep(1);
+ }
+
+ idevice_events_unsubscribe(context);
+ stop_logging();
+
+ fclose(packetlogger_file);
+
+ free(udid);
+
+ return 0;
+}
diff --git a/tools/idevicecrashreport.c b/tools/idevicecrashreport.c
index 5d2e21d..09bd537 100644
--- a/tools/idevicecrashreport.c
+++ b/tools/idevicecrashreport.c
@@ -24,14 +24,17 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicecrashreport"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <getopt.h>
#ifndef WIN32
#include <signal.h>
#endif
-#include "common/utils.h"
+#include <libimobiledevice-glue/utils.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
@@ -45,6 +48,9 @@
#define S_IFSOCK S_IFREG
#endif
+#define CRASH_REPORT_MOVER_SERVICE "com.apple.crashreportmover"
+#define CRASH_REPORT_COPY_MOBILE_SERVICE "com.apple.crashreportcopymobile"
+
const char* target_directory = NULL;
static int extract_raw_crash_reports = 0;
static int keep_crash_reports = 0;
@@ -75,7 +81,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);
@@ -98,7 +104,7 @@ static int extract_raw_crash_report(const char* filename)
return res;
}
-static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char* device_directory, const char* host_directory)
+static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char* device_directory, const char* host_directory, const char* filename_filter)
{
afc_error_t afc_error;
int k;
@@ -146,10 +152,18 @@ 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
+ /* replace every ':' with '-' since ':' is an illegal character for file names in windows */
+ char* current_pos = strchr(list[k], ':');
+ while (current_pos) {
+ *current_pos = '-';
+ current_pos = strchr(current_pos, ':');
+ }
+#endif
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 {
@@ -229,12 +243,16 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
#else
mkdir(target_filename, 0755);
#endif
- res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename);
+ res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename, filename_filter);
/* remove directory from device */
if (!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;
+ }
+
/* copy file to host */
afc_error = afc_file_open(afc, source_filename, AFC_FOPEN_RDONLY, &handle);
if(afc_error != AFC_E_SUCCESS) {
@@ -296,20 +314,27 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
return res;
}
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
- printf("Move crash reports from device to a local DIRECTORY.\n\n");
- printf(" -e, --extract\t\textract raw crash report into separate '.crash' file\n");
- printf(" -k, --keep\t\tcopy but do not remove crash reports from device\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Move crash reports from device to a local DIRECTORY.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -n, --network connect to network device\n"
+ " -e, --extract extract raw crash report into separate '.crash' file\n"
+ " -k, --keep copy but do not remove crash reports from device\n"
+ " -d, --debug enable communication debugging\n"
+ " -f, --filter NAME filter crash reports by NAME (case sensitive)\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
int main(int argc, char* argv[])
@@ -322,73 +347,97 @@ int main(int argc, char* argv[])
lockdownd_error_t lockdownd_error = LOCKDOWN_E_SUCCESS;
afc_error_t afc_error = AFC_E_SUCCESS;
- int i;
const char* udid = NULL;
+ int use_network = 0;
+ const char* filename_filter = 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' },
+ { "filter", required_argument, NULL, 'f' },
+ { "extract", no_argument, NULL, 'e' },
+ { "keep", no_argument, NULL, 'k' },
+ { NULL, 0, NULL, 0}
+ };
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
+
/* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:nvf:ek", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return 0;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = argv[i];
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
+ udid = optarg;
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'h':
+ print_usage(argc, argv, 0);
return 0;
- }
- else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--extract")) {
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ return 0;
+ case 'f':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: filter argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ filename_filter = optarg;
+ break;
+ case 'e':
extract_raw_crash_reports = 1;
- continue;
- }
- else if (!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keep")) {
+ break;
+ case 'k':
keep_crash_reports = 1;
- continue;
- }
- else if (target_directory == NULL) {
- target_directory = argv[i];
- continue;
- }
- else {
- print_usage(argc, argv);
- return 0;
+ break;
+ default:
+ print_usage(argc, argv, 1);
+ return 2;
}
}
+ argc -= optind;
+ argv += optind;
/* ensure a target directory was supplied */
- if (!target_directory) {
- print_usage(argc, argv);
- return 0;
+ if (!argv[0]) {
+ fprintf(stderr, "ERROR: missing target directory.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
+ target_directory = argv[0];
/* check if target directory exists */
if (!file_exists(target_directory)) {
fprintf(stderr, "ERROR: Directory '%s' does not exist.\n", target_directory);
- print_usage(argc, argv);
- return 0;
+ return 1;
}
- device_error = idevice_new(&device, udid);
+ device_error = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
if (device_error != IDEVICE_E_SUCCESS) {
if (udid) {
- printf("No device found with udid %s, is it plugged in?\n", udid);
+ printf("No device found with udid %s.\n", udid);
} else {
- printf("No device found, is it plugged in?\n");
+ printf("No device found.\n");
}
return -1;
}
- lockdownd_error = lockdownd_client_new_with_handshake(device, &lockdownd, "idevicecrashreport");
+ lockdownd_error = lockdownd_client_new_with_handshake(device, &lockdownd, TOOL_NAME);
if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lockdownd_error);
idevice_free(device);
@@ -397,8 +446,9 @@ int main(int argc, char* argv[])
/* start crash log mover service */
lockdownd_service_descriptor_t service = NULL;
- lockdownd_error = lockdownd_start_service(lockdownd, "com.apple.crashreportmover", &service);
+ lockdownd_error = lockdownd_start_service(lockdownd, CRASH_REPORT_MOVER_SERVICE, &service);
if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Could not start service %s: %s\n", CRASH_REPORT_MOVER_SERVICE, lockdownd_strerror(lockdownd_error));
lockdownd_client_free(lockdownd);
idevice_free(device);
return -1;
@@ -425,10 +475,10 @@ int main(int argc, char* argv[])
if (service_error == SERVICE_E_SUCCESS || service_error == SERVICE_E_TIMEOUT) {
attempts++;
continue;
- } else {
- fprintf(stderr, "ERROR: Crash logs could not be moved. Connection interrupted (%d).\n", service_error);
- break;
}
+
+ fprintf(stderr, "ERROR: Crash logs could not be moved. Connection interrupted (%d).\n", service_error);
+ break;
}
service_client_free(svcmove);
free(ping);
@@ -440,8 +490,9 @@ int main(int argc, char* argv[])
return -1;
}
- lockdownd_error = lockdownd_start_service(lockdownd, "com.apple.crashreportcopymobile", &service);
+ lockdownd_error = lockdownd_start_service(lockdownd, CRASH_REPORT_COPY_MOBILE_SERVICE, &service);
if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Could not start service %s: %s\n", CRASH_REPORT_COPY_MOBILE_SERVICE, lockdownd_strerror(lockdownd_error));
lockdownd_client_free(lockdownd);
idevice_free(device);
return -1;
@@ -462,7 +513,7 @@ int main(int argc, char* argv[])
}
/* recursively copy crash reports from the device to a local directory */
- if (afc_client_copy_and_remove_crash_reports(afc, ".", target_directory) < 0) {
+ if (afc_client_copy_and_remove_crash_reports(afc, ".", target_directory, filename_filter) < 0) {
fprintf(stderr, "ERROR: Failed to get crash reports from device.\n");
afc_client_free(afc);
idevice_free(device);
diff --git a/tools/idevicedate.c b/tools/idevicedate.c
index 4de90b6..d05f63e 100644
--- a/tools/idevicedate.c
+++ b/tools/idevicedate.c
@@ -23,9 +23,12 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicedate"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
#include <time.h>
#if HAVE_LANGINFO_CODESET
#include <langinfo.h>
@@ -38,27 +41,38 @@
#include <libimobiledevice/lockdown.h>
#ifdef _DATE_FMT
-#define DATE_FMT_LANGINFO() nl_langinfo (_DATE_FMT)
+#define DATE_FMT_LANGINFO nl_langinfo (_DATE_FMT)
+#else
+#ifdef WIN32
+#define DATE_FMT_LANGINFO "%a %b %#d %H:%M:%S %Z %Y"
#else
-#define DATE_FMT_LANGINFO() ""
+#define DATE_FMT_LANGINFO "%a %b %e %H:%M:%S %Z %Y"
+#endif
#endif
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
- printf("Display the current date or set it on a device.\n\n");
- printf("NOTE: Setting the time on iOS 6 and later is only supported\n");
- printf(" in the setup wizard screens before device activation.\n\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -s, --set TIMESTAMP\tset UTC time described by TIMESTAMP\n");
- printf(" -c, --sync\t\tset time of device to current system time\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ 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"
+ "Display the current date or set it on a device.\n"
+ "\n"
+ "NOTE: Setting the time on iOS 6 and later is only supported\n"
+ " in the setup wizard screens before device activation.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -n, --network connect to network device\n"
+ " -s, --set TIMESTAMP set UTC time described by TIMESTAMP\n"
+ " -c, --sync set time of device to current system time\n"
+ " -d, --debug enable communication debugging\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
int main(int argc, char *argv[])
@@ -67,89 +81,95 @@ int main(int argc, char *argv[])
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
idevice_t device = NULL;
idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
- int i;
const char* udid = NULL;
+ int use_network = 0;
time_t setdate = 0;
plist_t node = NULL;
int node_type = -1;
uint64_t datetime = 0;
time_t rawtime;
struct tm * tmp;
- char const *format = NULL;
char buffer[80];
int result = 0;
+ int c = 0;
+ const struct option longopts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+ { "network", no_argument, NULL, 'n' },
+ { "version", no_argument, NULL, 'v' },
+ { "set", required_argument, NULL, 's' },
+ { "sync", no_argument, NULL, 'c' },
+ { NULL, 0, NULL, 0}
+ };
+
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:nvs:c", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return 0;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = argv[i];
- continue;
- }
- else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--set")) {
- i++;
- if (!argv[i] || (strlen(argv[i]) <= 1)) {
- print_usage(argc, argv);
- return 0;
+ 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;
+ case 's':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: set argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- setdate = atoi(argv[i]);
+ setdate = atoi(optarg);
if (setdate == 0) {
- printf("ERROR: Invalid timestamp value.\n");
- print_usage(argc, argv);
+ fprintf(stderr, "ERROR: Invalid timestamp value.\n");
+ print_usage(argc, argv, 1);
return 0;
}
- continue;
- }
- else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--sync")) {
- i++;
+ break;
+ case 'c':
/* get current time */
setdate = time(NULL);
/* convert it to local time which sets timezone/daylight variables */
tmp = localtime(&setdate);
/* recalculate to make it UTC */
setdate = mktime(tmp);
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
- return 0;
- }
- else {
- print_usage(argc, argv);
- return 0;
- }
- }
-
- /* determine a date format */
- if (!format) {
- format = DATE_FMT_LANGINFO ();
- if (!*format) {
- format = "%a %b %e %H:%M:%S %Z %Y";
+ break;
+ default:
+ print_usage(argc, argv, 1);
+ return 2;
}
}
+ argc -= optind;
+ argv += optind;
- ret = idevice_new(&device, udid);
+ 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, is it plugged in?\n", udid);
+ printf("No device found with udid %s.\n", udid);
} else {
- printf("No device found, is it plugged in?\n");
+ printf("No device found.\n");
}
return -1;
}
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, "idevicedate"))) {
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
result = -1;
goto cleanup;
@@ -195,7 +215,7 @@ int main(int argc, char *argv[])
tmp = localtime(&rawtime);
/* finally we format and print the current date */
- strftime(buffer, 80, format, tmp);
+ strftime(buffer, 80, DATE_FMT_LANGINFO, tmp);
puts(buffer);
} else {
datetime = setdate;
@@ -217,7 +237,7 @@ int main(int argc, char *argv[])
if(lockdownd_set_value(client, NULL, "TimeIntervalSince1970", node) == LOCKDOWN_E_SUCCESS) {
tmp = localtime(&setdate);
- strftime(buffer, 80, format, tmp);
+ strftime(buffer, 80, DATE_FMT_LANGINFO, tmp);
puts(buffer);
} else {
printf("ERROR: Failed to set date on device.\n");
diff --git a/tools/idevicedebug.c b/tools/idevicedebug.c
index c7508e3..36c594e 100644
--- a/tools/idevicedebug.c
+++ b/tools/idevicedebug.c
@@ -23,6 +23,8 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicedebug"
+
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -30,6 +32,7 @@
#include <time.h>
#include <unistd.h>
#include <libgen.h>
+#include <getopt.h>
#ifdef WIN32
#include <windows.h>
@@ -42,9 +45,14 @@
#include <plist/plist.h>
#include "common/debug.h"
+static int debug_level = 0;
+
+#define log_debug(...) if (debug_level > 0) { printf(__VA_ARGS__); fputc('\n', stdout); }
+
enum cmd_mode {
CMD_NONE = 0,
- CMD_RUN
+ CMD_RUN,
+ CMD_KILL
};
static int quit_flag = 0;
@@ -55,7 +63,12 @@ static void on_signal(int sig)
quit_flag++;
}
-static instproxy_error_t instproxy_client_get_object_by_key_from_info_directionary_for_bundle_identifier(instproxy_client_t client, const char* appid, const char* key, plist_t* node)
+static int cancel_receive()
+{
+ return quit_flag;
+}
+
+static instproxy_error_t instproxy_client_get_object_by_key_from_info_dictionary_for_bundle_identifier(instproxy_client_t client, const char* appid, const char* key, plist_t* node)
{
if (!client || !appid || !key)
return INSTPROXY_E_INVALID_ARG;
@@ -93,7 +106,7 @@ static instproxy_error_t instproxy_client_get_object_by_key_from_info_directiona
if (object) {
*node = plist_copy(object);
} else {
- debug_info("key %s not found", key);
+ log_debug("key %s not found", key);
return INSTPROXY_E_OP_FAILED;
}
@@ -102,97 +115,90 @@ static instproxy_error_t instproxy_client_get_object_by_key_from_info_directiona
return INSTPROXY_E_SUCCESS;
}
-static debugserver_error_t debugserver_client_handle_response(debugserver_client_t client, char** response, int send_reply)
+static debugserver_error_t debugserver_client_handle_response(debugserver_client_t client, char** response, int* exit_status)
{
debugserver_error_t dres = DEBUGSERVER_E_SUCCESS;
- debugserver_command_t command = NULL;
char* o = NULL;
char* r = *response;
+ /* Documentation of response codes can be found here:
+ https://github.com/llvm/llvm-project/blob/4fe839ef3a51e0ea2e72ea2f8e209790489407a2/lldb/docs/lldb-gdb-remote.txt#L1269
+ */
+
if (r[0] == 'O') {
/* stdout/stderr */
debugserver_decode_string(r + 1, strlen(r) - 1, &o);
printf("%s", o);
fflush(stdout);
- if (o != NULL) {
- free(o);
- o = NULL;
- }
-
- free(*response);
- *response = NULL;
-
- if (!send_reply)
- return dres;
-
- /* send reply */
- debugserver_command_new("OK", 0, NULL, &command);
- dres = debugserver_client_send_command(client, command, response);
- debug_info("result: %d", dres);
- debugserver_command_free(command);
- command = NULL;
} else if (r[0] == 'T') {
/* thread stopped information */
- debug_info("Thread stopped. Details:\n%s", r + 1);
-
- free(*response);
- *response = NULL;
-
- if (!send_reply)
- return dres;
-
+ log_debug("Thread stopped. Details:\n%s", r + 1);
+ if (exit_status != NULL) {
+ /* "Thread stopped" seems to happen when assert() fails.
+ Use bash convention where signals cause an exit
+ status of 128 + signal
+ */
+ *exit_status = 128 + SIGABRT;
+ }
+ /* Break out of the loop. */
dres = DEBUGSERVER_E_UNKNOWN_ERROR;
- } else if (r[0] == 'E' || r[0] == 'W') {
- printf("%s: %s\n", (r[0] == 'E' ? "ERROR": "WARNING") , r + 1);
-
- free(*response);
- *response = NULL;
-
- if (!send_reply)
- return dres;
-
- /* send reply */
- debugserver_command_new("OK", 0, NULL, &command);
- dres = debugserver_client_send_command(client, command, response);
- debug_info("result: %d", dres);
- debugserver_command_free(command);
- command = NULL;
+ } else if (r[0] == 'E') {
+ printf("ERROR: %s\n", r + 1);
+ } else if (r[0] == 'W' || r[0] == 'X') {
+ /* process exited */
+ debugserver_decode_string(r + 1, strlen(r) - 1, &o);
+ if (o != NULL) {
+ printf("Exit %s: %u\n", (r[0] == 'W' ? "status" : "due to signal"), o[0]);
+ if (exit_status != NULL) {
+ /* Use bash convention where signals cause an
+ exit status of 128 + signal
+ */
+ *exit_status = o[0] + (r[0] == 'W' ? 0 : 128);
+ }
+ } else {
+ log_debug("Unable to decode exit status from %s", r);
+ dres = DEBUGSERVER_E_UNKNOWN_ERROR;
+ }
} else if (r && strlen(r) == 0) {
- if (!send_reply)
- return dres;
-
- free(*response);
- *response = NULL;
-
- /* no command */
- debugserver_command_new("OK", 0, NULL, &command);
- dres = debugserver_client_send_command(client, command, response);
- debug_info("result: %d", dres);
- debugserver_command_free(command);
- command = NULL;
+ log_debug("empty response");
} else {
- debug_info("ERROR: unhandled response", r);
+ log_debug("ERROR: unhandled response '%s'", r);
}
+ if (o != NULL) {
+ free(o);
+ o = NULL;
+ }
+
+ free(*response);
+ *response = NULL;
return dres;
}
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
- printf("Interact with the debugserver service of a device.\n\n");
- printf(" Where COMMAND is one of:\n");
- printf(" run BUNDLEID [ARGS...]\trun app with BUNDLEID and optional ARGS on device.\n");
- printf("\n");
- printf(" The following OPTIONS are accepted:\n");
- printf(" -e, --env NAME=VALUE\tset environment variable NAME to VALUE\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ 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"
+ "Interact with the debugserver service of a device.\n"
+ "\n"
+ "Where COMMAND is one of:\n"
+ " run BUNDLEID [ARGS...] run app with BUNDLEID and optional ARGS on device.\n"
+ " kill BUNDLEID kill app with BUNDLEID\n"
+ "\n"
+ "The following OPTIONS are accepted:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -n, --network connect to network device\n"
+ " --detach detach from app after launch, keeping it running\n"
+ " -e, --env NAME=VALUE set environment variable NAME to VALUE\n"
+ " -d, --debug enable communication debugging\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
int main(int argc, char *argv[])
@@ -203,9 +209,10 @@ int main(int argc, char *argv[])
instproxy_client_t instproxy_client = NULL;
debugserver_client_t debugserver_client = NULL;
int i;
- int debug_level = 0;
int cmd = CMD_NONE;
const char* udid = NULL;
+ int use_network = 0;
+ int detach_after_start = 0;
const char* bundle_identifier = NULL;
char* path = NULL;
char* working_directory = NULL;
@@ -217,6 +224,18 @@ int main(int argc, char *argv[])
debugserver_command_t command = NULL;
debugserver_error_t dres = DEBUGSERVER_E_UNKNOWN_ERROR;
+ 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' },
+ { "detach", no_argument, NULL, 1 },
+ { "env", required_argument, NULL, 'e' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+ };
+
/* map signals */
signal(SIGINT, on_signal);
signal(SIGTERM, on_signal);
@@ -225,26 +244,33 @@ int main(int argc, char *argv[])
signal(SIGPIPE, SIG_IGN);
#endif
- /* parse command line arguments */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:ne:v", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
debug_level++;
- idevice_set_debug_level(debug_level);
- continue;
- } else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- res = 0;
- goto cleanup;
+ if (debug_level > 1) {
+ idevice_set_debug_level(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 = argv[i];
- continue;
- } else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--env")) {
- i++;
- if (!argv[i] || (strlen(argv[i]) <= 1) || strchr(argv[i], '=') == NULL) {
- print_usage(argc, argv);
- res = 0;
+ udid = optarg;
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 1:
+ detach_after_start = 1;
+ break;
+ case 'e':
+ if (!*optarg || strchr(optarg, '=') == NULL) {
+ fprintf(stderr, "ERROR: environment variables need to be specified as -e KEY=VALUE\n");
+ print_usage(argc, argv, 1);
+ res = 2;
goto cleanup;
}
/* add environment variable */
@@ -252,238 +278,323 @@ int main(int argc, char *argv[])
newlist = malloc((environment_count + 1) * sizeof(char*));
else
newlist = realloc(environment, (environment_count + 1) * sizeof(char*));
- newlist[environment_count++] = strdup(argv[i]);
+ newlist[environment_count++] = strdup(optarg);
environment = newlist;
- continue;
- } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
+ break;
+ case 'h':
+ print_usage(argc, argv, 0);
res = 0;
goto cleanup;
- } else if (!strcmp(argv[i], "run")) {
- cmd = CMD_RUN;
-
- i++;
- if (!argv[i]) {
- /* make sure at least the bundle identifier was provided */
- printf("Please supply the bundle identifier of the app to run.\n");
- print_usage(argc, argv);
- res = 0;
- goto cleanup;
- }
- /* read bundle identifier */
- bundle_identifier = argv[i];
break;
- } else {
- print_usage(argc, argv);
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
res = 0;
goto cleanup;
+ break;
+ default:
+ print_usage(argc, argv, 1);
+ res = 2;
+ goto cleanup;
+ break;
}
}
+ argc -= optind;
+ argv += optind;
- if (environment) {
- newlist = realloc(environment, (environment_count + 1) * sizeof(char*));
- newlist[environment_count] = NULL;
- environment = newlist;
+ if (argc < 1) {
+ fprintf(stderr, "ERROR: Missing command.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+
+ if (!strcmp(argv[0], "run")) {
+ cmd = CMD_RUN;
+ if (argc < 2) {
+ /* make sure at least the bundle identifier was provided */
+ fprintf(stderr, "ERROR: Please supply the bundle identifier of the app to run.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ res = 2;
+ goto cleanup;
+ }
+ /* read bundle identifier */
+ bundle_identifier = argv[1];
+ i = 1;
+ } else if (!strcmp(argv[0], "kill")) {
+ cmd = CMD_KILL;
+ if (argc < 2) {
+ /* make sure at least the bundle identifier was provided */
+ fprintf(stderr, "ERROR: Please supply the bundle identifier of the app to run.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ res = 2;
+ goto cleanup;
+ }
+ /* read bundle identifier */
+ bundle_identifier = argv[1];
+ i = 1;
}
/* verify options */
if (cmd == CMD_NONE) {
- print_usage(argc, argv);
+ fprintf(stderr, "ERROR: Unsupported command specified.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ res = 2;
goto cleanup;
}
+ if (environment) {
+ newlist = realloc(environment, (environment_count + 1) * sizeof(char*));
+ newlist[environment_count] = NULL;
+ environment = newlist;
+ }
+
/* connect to the device */
- ret = idevice_new(&device, udid);
+ 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, is it plugged in?\n", udid);
+ printf("No device found with udid %s.\n", udid);
} else {
- printf("No device found, is it plugged in?\n");
+ printf("No device found.\n");
}
goto cleanup;
}
- switch (cmd) {
- case CMD_RUN:
- default:
- /* get the path to the app and it's working directory */
- if (instproxy_client_start_service(device, &instproxy_client, "idevicerun") != INSTPROXY_E_SUCCESS) {
- fprintf(stderr, "Could not start installation proxy service.\n");
- goto cleanup;
- }
- plist_t container = NULL;
- instproxy_client_get_object_by_key_from_info_directionary_for_bundle_identifier(instproxy_client, bundle_identifier, "Container", &container);
- instproxy_client_get_path_for_bundle_identifier(instproxy_client, bundle_identifier, &path);
- instproxy_client_free(instproxy_client);
- instproxy_client = NULL;
-
- if (container && (plist_get_node_type(container) == PLIST_STRING)) {
- plist_get_string_val(container, &working_directory);
- debug_info("working_directory: %s\n", working_directory);
- plist_free(container);
- } else {
- plist_free(container);
- fprintf(stderr, "Could not determine container path for bundle identifier %s.\n", bundle_identifier);
- goto cleanup;
- }
+ /* get the path to the app and it's working directory */
+ if (instproxy_client_start_service(device, &instproxy_client, TOOL_NAME) != INSTPROXY_E_SUCCESS) {
+ fprintf(stderr, "Could not start installation proxy service.\n");
+ goto cleanup;
+ }
+
+ instproxy_client_get_path_for_bundle_identifier(instproxy_client, bundle_identifier, &path);
+ if (!path) {
+ fprintf(stderr, "Invalid bundle identifier: %s\n", bundle_identifier);
+ goto cleanup;
+ }
+
+ plist_t container = NULL;
+ instproxy_client_get_object_by_key_from_info_dictionary_for_bundle_identifier(instproxy_client, bundle_identifier, "Container", &container);
+ instproxy_client_free(instproxy_client);
+ instproxy_client = NULL;
+
+ if (container && (plist_get_node_type(container) == PLIST_STRING)) {
+ plist_get_string_val(container, &working_directory);
+ log_debug("working_directory: %s\n", working_directory);
+ plist_free(container);
+ } else {
+ plist_free(container);
+ fprintf(stderr, "Could not determine container path for bundle identifier %s.\n", bundle_identifier);
+ goto cleanup;
+ }
+
+ /* start and connect to debugserver */
+ if (debugserver_client_start_service(device, &debugserver_client, TOOL_NAME) != DEBUGSERVER_E_SUCCESS) {
+ fprintf(stderr,
+ "Could not start com.apple.debugserver!\n"
+ "Please make sure to mount the developer disk image first:\n"
+ " 1) Get the iOS version from `ideviceinfo -k ProductVersion`.\n"
+ " 2) Find the matching iPhoneOS DeveloperDiskImage.dmg files.\n"
+ " 3) Run `ideviceimagemounter` with the above path.\n");
+ goto cleanup;
+ }
+
+ /* set receive params */
+ if (debugserver_client_set_receive_params(debugserver_client, cancel_receive, 250) != DEBUGSERVER_E_SUCCESS) {
+ fprintf(stderr, "Error in debugserver_client_set_receive_params\n");
+ goto cleanup;
+ }
- /* start and connect to debugserver */
- if (debugserver_client_start_service(device, &debugserver_client, "idevicerun") != DEBUGSERVER_E_SUCCESS) {
- fprintf(stderr,
- "Could not start com.apple.debugserver!\n"
- "Please make sure to mount the developer disk image first:\n"
- " 1) Get the iOS version from `ideviceinfo -k ProductVersion`.\n"
- " 2) Find the matching iPhoneOS DeveloperDiskImage.dmg files.\n"
- " 3) Run `ideviceimagemounter` with the above path.\n");
+ /* enable logging for the session in debug mode */
+ if (debug_level) {
+ log_debug("Setting logging bitmask...");
+ debugserver_command_new("QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE|LOG_RNB_PACKETS;", 0, NULL, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ if (response) {
+ if (strncmp(response, "OK", 2) != 0) {
+ debugserver_client_handle_response(debugserver_client, &response, NULL);
goto cleanup;
}
+ free(response);
+ response = NULL;
+ }
+ }
- /* enable logging for the session in debug mode */
- if (debug_level) {
- debug_info("Setting logging bitmask...");
- debugserver_command_new("QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE|LOG_RNB_PACKETS", 0, NULL, &command);
- dres = debugserver_client_send_command(debugserver_client, command, &response);
- debugserver_command_free(command);
- command = NULL;
- if (response) {
- if (strncmp(response, "OK", 2)) {
- debugserver_client_handle_response(debugserver_client, &response, 0);
- goto cleanup;
- }
- free(response);
- response = NULL;
- }
- }
+ /* set maximum packet size */
+ log_debug("Setting maximum packet size...");
+ char* packet_size[2] = { (char*)"1024", NULL};
+ debugserver_command_new("QSetMaxPacketSize:", 1, packet_size, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ if (response) {
+ if (strncmp(response, "OK", 2) != 0) {
+ debugserver_client_handle_response(debugserver_client, &response, NULL);
+ goto cleanup;
+ }
+ free(response);
+ response = NULL;
+ }
- /* set maximum packet size */
- debug_info("Setting maximum packet size...");
- char* packet_size[2] = {strdup("1024"), NULL};
- debugserver_command_new("QSetMaxPacketSize:", 1, packet_size, &command);
- free(packet_size[0]);
- dres = debugserver_client_send_command(debugserver_client, command, &response);
- debugserver_command_free(command);
- command = NULL;
- if (response) {
- if (strncmp(response, "OK", 2)) {
- debugserver_client_handle_response(debugserver_client, &response, 0);
- goto cleanup;
- }
- free(response);
- response = NULL;
- }
+ /* set working directory */
+ log_debug("Setting working directory...");
+ char* working_dir[2] = {working_directory, NULL};
+ debugserver_command_new("QSetWorkingDir:", 1, working_dir, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ if (response) {
+ if (strncmp(response, "OK", 2) != 0) {
+ debugserver_client_handle_response(debugserver_client, &response, NULL);
+ goto cleanup;
+ }
+ free(response);
+ response = NULL;
+ }
+
+ /* set environment */
+ if (environment) {
+ log_debug("Setting environment...");
+ for (environment_index = 0; environment_index < environment_count; environment_index++) {
+ log_debug("setting environment variable: %s", environment[environment_index]);
+ debugserver_client_set_environment_hex_encoded(debugserver_client, environment[environment_index], NULL);
+ }
+ }
+
+ /* set arguments and run app */
+ log_debug("Setting argv...");
+ i++; /* i is the offset of the bundle identifier, thus skip it */
+ int app_argc = (argc - i + 2);
+ char **app_argv = (char**)malloc(sizeof(char*) * app_argc);
+ app_argv[0] = path;
+ log_debug("app_argv[%d] = %s", 0, app_argv[0]);
+ app_argc = 1;
+ while (i < argc && argv && argv[i]) {
+ log_debug("app_argv[%d] = %s", app_argc, argv[i]);
+ app_argv[app_argc++] = argv[i];
+ i++;
+ }
+ app_argv[app_argc] = NULL;
+ debugserver_client_set_argv(debugserver_client, app_argc, app_argv, NULL);
+ free(app_argv);
+
+ /* check if launch succeeded */
+ log_debug("Checking if launch succeeded...");
+ debugserver_command_new("qLaunchSuccess", 0, NULL, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ if (response) {
+ if (strncmp(response, "OK", 2) != 0) {
+ debugserver_client_handle_response(debugserver_client, &response, NULL);
+ goto cleanup;
+ }
+ free(response);
+ response = NULL;
+ }
- /* set working directory */
- debug_info("Setting working directory...");
- char* working_dir[2] = {working_directory, NULL};
- debugserver_command_new("QSetWorkingDir:", 1, working_dir, &command);
- dres = debugserver_client_send_command(debugserver_client, command, &response);
+ if (cmd == CMD_KILL) {
+ debugserver_command_new("k", 0, NULL, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ goto cleanup;
+ } else
+ if (cmd == CMD_RUN) {
+ if (detach_after_start) {
+ log_debug("Detaching from app");
+ debugserver_command_new("D", 0, NULL, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
debugserver_command_free(command);
command = NULL;
- if (response) {
- if (strncmp(response, "OK", 2)) {
- debugserver_client_handle_response(debugserver_client, &response, 0);
- goto cleanup;
- }
- free(response);
- response = NULL;
- }
- /* set environment */
- if (environment) {
- debug_info("Setting environment...");
- for (environment_index = 0; environment_index < environment_count; environment_index++) {
- debug_info("setting environment variable: %s", environment[environment_index]);
- debugserver_client_set_environment_hex_encoded(debugserver_client, environment[environment_index], NULL);
- }
- }
+ res = (dres == DEBUGSERVER_E_SUCCESS) ? 0: -1;
+ goto cleanup;
+ }
- /* set arguments and run app */
- debug_info("Setting argv...");
- i++; /* i is the offset of the bundle identifier, thus skip it */
- int app_argc = (argc - i + 2);
- char **app_argv = (char**)malloc(sizeof(char*) * app_argc);
- app_argv[0] = path;
- debug_info("app_argv[%d] = %s", 0, app_argv[0]);
- app_argc = 1;
- while (i < argc && argv && argv[i]) {
- debug_info("app_argv[%d] = %s", app_argc, argv[i]);
- app_argv[app_argc++] = argv[i];
- i++;
+ /* set thread */
+ log_debug("Setting thread...");
+ debugserver_command_new("Hc0", 0, NULL, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ if (response) {
+ if (strncmp(response, "OK", 2) != 0) {
+ debugserver_client_handle_response(debugserver_client, &response, NULL);
+ goto cleanup;
}
- app_argv[app_argc] = NULL;
- debugserver_client_set_argv(debugserver_client, app_argc, app_argv, NULL);
- free(app_argv);
-
- /* check if launch succeeded */
- debug_info("Checking if launch succeeded...");
- debugserver_command_new("qLaunchSuccess", 0, NULL, &command);
- dres = debugserver_client_send_command(debugserver_client, command, &response);
- debugserver_command_free(command);
- command = NULL;
- if (response) {
- if (strncmp(response, "OK", 2)) {
- debugserver_client_handle_response(debugserver_client, &response, 0);
- goto cleanup;
- }
- free(response);
- response = NULL;
+ free(response);
+ response = NULL;
+ }
+
+ /* continue running process */
+ log_debug("Continue running process...");
+ debugserver_command_new("c", 0, NULL, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ log_debug("Continue response: %s", response);
+
+ /* main loop which is parsing/handling packets during the run */
+ log_debug("Entering run loop...");
+ while (!quit_flag) {
+ if (dres != DEBUGSERVER_E_SUCCESS) {
+ log_debug("failed to receive response; error %d", dres);
+ break;
}
- /* set thread */
- debug_info("Setting thread...");
- debugserver_command_new("Hc0", 0, NULL, &command);
- dres = debugserver_client_send_command(debugserver_client, command, &response);
- debugserver_command_free(command);
- command = NULL;
if (response) {
- if (strncmp(response, "OK", 2)) {
- debugserver_client_handle_response(debugserver_client, &response, 0);
- goto cleanup;
+ log_debug("response: %s", response);
+ if (strncmp(response, "OK", 2) != 0) {
+ dres = debugserver_client_handle_response(debugserver_client, &response, &res);
+ if (dres != DEBUGSERVER_E_SUCCESS) {
+ log_debug("failed to process response; error %d; %s", dres, response);
+ break;
+ }
}
- free(response);
- response = NULL;
+ }
+ if (res >= 0) {
+ goto cleanup;
}
- /* continue running process */
- debug_info("Continue running process...");
- debugserver_command_new("c", 0, NULL, &command);
- dres = debugserver_client_send_command(debugserver_client, command, &response);
- debugserver_command_free(command);
- command = NULL;
-
- /* main loop which is parsing/handling packets during the run */
- debug_info("Entering run loop...");
- while (!quit_flag) {
- if (dres != DEBUGSERVER_E_SUCCESS) {
- debug_info("failed to receive response");
- break;
- }
+ dres = debugserver_client_receive_response(debugserver_client, &response, NULL);
+ }
- if (response) {
- debug_info("response: %s", response);
- dres = debugserver_client_handle_response(debugserver_client, &response, 1);
- }
+ /* ignore quit_flag after this point */
+ if (debugserver_client_set_receive_params(debugserver_client, NULL, 5000) != DEBUGSERVER_E_SUCCESS) {
+ fprintf(stderr, "Error in debugserver_client_set_receive_params\n");
+ goto cleanup;
+ }
- sleep(1);
+ /* interrupt execution */
+ debugserver_command_new("\x03", 0, NULL, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ if (response) {
+ if (strncmp(response, "OK", 2) != 0) {
+ debugserver_client_handle_response(debugserver_client, &response, NULL);
}
+ free(response);
+ response = NULL;
+ }
- /* kill process after we finished */
- debug_info("Killing process...");
- debugserver_command_new("k", 0, NULL, &command);
- dres = debugserver_client_send_command(debugserver_client, command, &response);
- debugserver_command_free(command);
- command = NULL;
- if (response) {
- if (strncmp(response, "OK", 2)) {
- debugserver_client_handle_response(debugserver_client, &response, 0);
- goto cleanup;
- }
- free(response);
- response = NULL;
+ /* kill process after we finished */
+ log_debug("Killing process...");
+ debugserver_command_new("k", 0, NULL, &command);
+ dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
+ debugserver_command_free(command);
+ command = NULL;
+ if (response) {
+ if (strncmp(response, "OK", 2) != 0) {
+ debugserver_client_handle_response(debugserver_client, &response, NULL);
}
+ free(response);
+ response = NULL;
+ }
+ if (res < 0) {
res = (dres == DEBUGSERVER_E_SUCCESS) ? 0: -1;
- break;
+ }
}
cleanup:
diff --git a/tools/idevicedebugserverproxy.c b/tools/idevicedebugserverproxy.c
index e99d0bf..9fe7051 100644
--- a/tools/idevicedebugserverproxy.c
+++ b/tools/idevicedebugserverproxy.c
@@ -2,6 +2,7 @@
* idevicedebugserverproxy.c
* Proxy a debugserver connection from device for remote debugging
*
+ * Copyright (c) 2021 Nikias Bassen, All Rights Reserved.
* Copyright (c) 2012 Martin Szulecki All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
@@ -23,34 +24,48 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicedebugserverproxy"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
#include <errno.h>
#include <signal.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#else
+#include <sys/select.h>
+#endif
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/debugserver.h>
-#include "common/socket.h"
-#include "common/thread.h"
+#include <libimobiledevice-glue/socket.h>
+#include <libimobiledevice-glue/thread.h>
+
+#ifndef ETIMEDOUT
+#define ETIMEDOUT 138
+#endif
#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;
idevice_t device;
debugserver_client_t debugserver_client;
- volatile int stop_ctod;
- volatile int stop_dtoc;
} socket_info_t;
struct thread_info {
THREAD_T th;
+ int client_fd;
struct thread_info *next;
};
@@ -63,167 +78,114 @@ static void clean_exit(int sig)
quit_flag++;
}
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] <PORT>\n", (name ? name + 1: argv[0]));
- printf("Proxy debugserver connection from device to a local socket at PORT.\n\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [PORT]\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Proxy debugserver connection from device to a local socket at PORT.\n"
+ "If PORT is omitted, the next available port will be used and printed\n"
+ "to stdout.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -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"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
-static void *thread_device_to_client(void *data)
-{
- socket_info_t* socket_info = (socket_info_t*)data;
- debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
-
- int recv_len;
- int sent;
- char buffer[131072];
-
- debug("%s: started thread...\n", __func__);
-
- debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
+static int intercept_packet(char *packet, ssize_t *packet_len) {
+ static const char kReqLaunchServer[] = "$qLaunchGDBServer;#4b";
- while (!quit_flag && !socket_info->stop_dtoc && socket_info->client_fd > 0) {
- debug("%s: receiving data from device...\n", __func__);
-
- res = debugserver_client_receive_with_timeout(socket_info->debugserver_client, buffer, sizeof(buffer), (uint32_t*)&recv_len, 5000);
-
- if (recv_len <= 0) {
- if (recv_len == 0 && res == DEBUGSERVER_E_SUCCESS) {
- // try again
- continue;
- } else {
- fprintf(stderr, "recv failed: %s\n", strerror(errno));
- break;
- }
- } else {
- /* send to device */
- debug("%s: sending data to client...\n", __func__);
- sent = socket_send(socket_info->client_fd, buffer, recv_len);
- if (sent < recv_len) {
- if (sent <= 0) {
- fprintf(stderr, "send failed: %s\n", strerror(errno));
- break;
- } else {
- fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
- }
- } else {
- // sending succeeded, receive from device
- debug("%s: pushed %d bytes to client\n", __func__, sent);
- }
- }
+ 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;
}
-
- debug("%s: shutting down...\n", __func__);
-
- socket_shutdown(socket_info->client_fd, SHUT_RDWR);
- socket_close(socket_info->client_fd);
-
- socket_info->client_fd = -1;
- socket_info->stop_ctod = 1;
-
- return NULL;
+ 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 *thread_client_to_device(void *data)
+static void* connection_handler(void* data)
{
+ debugserver_error_t derr = DEBUGSERVER_E_SUCCESS;
socket_info_t* socket_info = (socket_info_t*)data;
- debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
+ const int bufsize = 65536;
+ char* buf;
- int recv_len;
- int sent;
- char buffer[131072];
- THREAD_T dtoc;
+ int client_fd = socket_info->client_fd;
- debug("%s: started thread...\n", __func__);
+ debug("%s: client_fd = %d\n", __func__, client_fd);
- debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
+ derr = debugserver_client_start_service(socket_info->device, &socket_info->debugserver_client, TOOL_NAME);
+ if (derr != DEBUGSERVER_E_SUCCESS) {
+ fprintf(stderr, "Could not start debugserver on device!\nPlease make sure to mount a developer disk image first.\n");
+ return NULL;
+ }
- /* spawn server to client thread */
- socket_info->stop_dtoc = 0;
- if (thread_new(&dtoc, thread_device_to_client, data) != 0) {
- fprintf(stderr, "Failed to start device to client thread...\n");
+ buf = malloc(bufsize);
+ if (!buf) {
+ fprintf(stderr, "Failed to allocate buffer\n");
+ return NULL;
}
- while (!quit_flag && !socket_info->stop_ctod && socket_info->client_fd > 0) {
- debug("%s: receiving data from client...\n", __func__);
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(client_fd, &fds);
- /* attempt to read incoming data from client */
- recv_len = socket_receive_timeout(socket_info->client_fd, buffer, sizeof(buffer), 0, 5000);
+ int dtimeout = 1;
- /* any data received? */
- if (recv_len <= 0) {
- if (recv_len == 0) {
- /* try again */
- continue;
- } else {
- fprintf(stderr, "Receive failed: %s\n", strerror(errno));
+ while (!quit_flag) {
+ ssize_t n = socket_receive_timeout(client_fd, buf, bufsize, 0, 1);
+ if (n != -ETIMEDOUT) {
+ if (n < 0) {
+ fprintf(stderr, "Failed to read from client fd: %s\n", strerror(-n));
+ break;
+ } else if (n == 0) {
+ fprintf(stderr, "connection closed\n");
break;
}
- } else {
- /* forward data to device */
- debug("%s: sending data to device...\n", __func__);
- res = debugserver_client_send(socket_info->debugserver_client, buffer, recv_len, (uint32_t*)&sent);
-
- if (sent < recv_len || res != DEBUGSERVER_E_SUCCESS) {
- if (sent <= 0) {
- fprintf(stderr, "send failed: %s\n", strerror(errno));
- break;
- } else {
- fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
- }
+ 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);
+ }
+ do {
+ uint32_t r = 0;
+ derr = debugserver_client_receive_with_timeout(socket_info->debugserver_client, buf, bufsize, &r, dtimeout);
+ if (r > 0) {
+ socket_send(client_fd, buf, r);
+ dtimeout = 1;
+ } else if (derr == DEBUGSERVER_E_TIMEOUT) {
+ dtimeout = 5;
+ break;
} else {
- // sending succeeded, receive from device
- debug("%s: sent %d bytes to device\n", __func__, sent);
+ fprintf(stderr, "debugserver connection closed\n");
+ break;
}
+ } while (derr == DEBUGSERVER_E_SUCCESS);
+ if (derr != DEBUGSERVER_E_TIMEOUT && derr != DEBUGSERVER_E_SUCCESS) {
+ break;
}
}
-
- debug("%s: shutting down...\n", __func__);
-
- socket_shutdown(socket_info->client_fd, SHUT_RDWR);
- socket_close(socket_info->client_fd);
-
- socket_info->client_fd = -1;
- socket_info->stop_dtoc = 1;
-
- /* join other thread to allow it to stop */
- thread_join(dtoc);
- thread_free(dtoc);
-
- return NULL;
-}
-
-static void* connection_handler(void* data)
-{
- debugserver_error_t derr = DEBUGSERVER_E_SUCCESS;
- socket_info_t* socket_info = (socket_info_t*)data;
- THREAD_T ctod;
-
- debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
-
- derr = debugserver_client_start_service(socket_info->device, &socket_info->debugserver_client, "idevicedebugserverproxy");
- if (derr != DEBUGSERVER_E_SUCCESS) {
- fprintf(stderr, "Could not start debugserver on device!\nPlease make sure to mount a developer disk image first.\n");
- return NULL;
- }
-
- /* spawn client to device thread */
- socket_info->stop_ctod = 0;
- if (thread_new(&ctod, thread_client_to_device, data) != 0) {
- fprintf(stderr, "Failed to start client to device thread...\n");
- }
-
- /* join the fun */
- thread_join(ctod);
- thread_free(ctod);
+ free(buf);
debug("%s: shutting down...\n", __func__);
@@ -243,10 +205,19 @@ int main(int argc, char *argv[])
idevice_t device = NULL;
thread_info_t *thread_list = NULL;
const char* udid = NULL;
- uint16_t local_port = 0;
+ int use_network = 0;
int server_fd;
int result = EXIT_SUCCESS;
- int i;
+ 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' },
+ { "lldb", no_argument, NULL, 'l' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
+ };
#ifndef WIN32
struct sigaction sa;
@@ -271,63 +242,76 @@ int main(int argc, char *argv[])
#endif
/* parse cmdline arguments */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
debug_mode = 1;
idevice_set_debug_level(1);
socket_set_verbose(3);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return 0;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = argv[i];
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
- return EXIT_SUCCESS;
- }
- else if (atoi(argv[i]) > 0) {
- local_port = atoi(argv[i]);
- continue;
- }
- else {
- print_usage(argc, argv);
- return EXIT_SUCCESS;
+ udid = optarg;
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'l':
+ support_lldb = 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;
- /* a PORT is mandatory */
- if (!local_port) {
- fprintf(stderr, "Please specify a PORT.\n");
- print_usage(argc, argv);
- goto leave_cleanup;
+ if (argv[0] && (atoi(argv[0]) > 0)) {
+ local_port = atoi(argv[0]);
}
/* start services and connect to device */
- ret = idevice_new(&device, udid);
+ ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
if (ret != IDEVICE_E_SUCCESS) {
if (udid) {
- fprintf(stderr, "No device found with udid %s, is it plugged in?\n", udid);
+ fprintf(stderr, "No device found with udid %s.\n", udid);
} else {
- fprintf(stderr, "No device found, is it plugged in?\n");
+ fprintf(stderr, "No device found.\n");
}
result = EXIT_FAILURE;
goto leave_cleanup;
}
/* create local socket */
- server_fd = socket_create(local_port);
+ server_fd = socket_create("127.0.0.1", local_port);
if (server_fd < 0) {
fprintf(stderr, "Could not create socket\n");
result = EXIT_FAILURE;
goto leave_cleanup;
}
+ if (local_port == 0) {
+ /* The user asked for any available port. Report the actual port. */
+ uint16_t port;
+ if (0 > socket_get_socket_port(server_fd, &port)) {
+ fprintf(stderr, "Could not determine socket port\n");
+ result = EXIT_FAILURE;
+ goto leave_cleanup;
+ }
+ printf("Listening on port %d\n", port);
+ }
+
while (!quit_flag) {
debug("%s: Waiting for connection on local port %d\n", __func__, local_port);
@@ -344,6 +328,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
+ el->client_fd = client_fd;
el->next = NULL;
if (thread_list) {
@@ -373,6 +358,8 @@ int main(int argc, char *argv[])
/* join and clean up threads */
while (thread_list) {
thread_info_t *el = thread_list;
+ socket_shutdown(el->client_fd, SHUT_RDWR);
+ socket_close(el->client_fd);
thread_join(el->th);
thread_free(el->th);
thread_list = el->next;
diff --git a/tools/idevicedevmodectl.c b/tools/idevicedevmodectl.c
new file mode 100644
index 0000000..bd1de6a
--- /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 bff1e1d..e699bc4 100644
--- a/tools/idevicediagnostics.c
+++ b/tools/idevicediagnostics.c
@@ -23,9 +23,12 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicediagnostics"
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <getopt.h>
#include <errno.h>
#include <time.h>
#ifndef WIN32
@@ -57,7 +60,34 @@ static void print_xml(plist_t node)
}
}
-void print_usage(int argc, char **argv);
+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"
+ "Use diagnostics interface of a device running iOS 4 or later.\n"
+ "\n"
+ "Where COMMAND is one of:\n"
+ " diagnostics [TYPE] print diagnostics information from device by TYPE (All, WiFi, GasGauge, NAND)\n"
+ " mobilegestalt KEY [...] print mobilegestalt keys passed as arguments separated by a space.\n"
+ " ioreg [PLANE] print IORegistry of device, optionally by PLANE (IODeviceTree, IOPower, IOService) (iOS 5+ only)\n"
+ " ioregentry [KEY] print IORegistry entry of device (AppleARMPMUCharger, ASPStorage, ...) (iOS 5+ only)\n"
+ " shutdown shutdown device\n"
+ " restart restart device\n"
+ " sleep put device into sleep mode (disconnects from host)\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 prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
+}
int main(int argc, char **argv)
{
@@ -66,134 +96,133 @@ int main(int argc, char **argv)
diagnostics_relay_client_t diagnostics_client = NULL;
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
lockdownd_service_descriptor_t service = NULL;
- int result = -1;
- int i;
+ int result = EXIT_FAILURE;
const char *udid = NULL;
+ int use_network = 0;
int cmd = CMD_NONE;
char* cmd_arg = NULL;
plist_t node = NULL;
plist_t keys = 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 */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- result = 0;
- goto cleanup;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = argv[i];
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
- result = 0;
- goto cleanup;
- }
- else if (!strcmp(argv[i], "sleep")) {
- cmd = CMD_SLEEP;
- }
- else if (!strcmp(argv[i], "restart")) {
- cmd = CMD_RESTART;
- }
- else if (!strcmp(argv[i], "shutdown")) {
- cmd = CMD_SHUTDOWN;
+ 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;
}
- else if (!strcmp(argv[i], "diagnostics")) {
- cmd = CMD_DIAGNOSTICS;
- /* read type */
- i++;
- if (!argv[i] || ((strcmp(argv[i], "All") != 0) && (strcmp(argv[i], "WiFi") != 0) && (strcmp(argv[i], "GasGauge") != 0) && (strcmp(argv[i], "NAND") != 0) && (strcmp(argv[i], "HDMI") != 0))) {
- if (argv[i] == NULL) {
- cmd_arg = strdup("All");
- continue;
- }
+ }
+ argc -= optind;
+ argv += optind;
- if (!strncmp(argv[i], "-", 1)) {
- cmd_arg = strdup("All");
- i--;
- continue;
- }
+ if (!argv[0]) {
+ fprintf(stderr, "ERROR: No command specified\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
- printf("Unknown TYPE %s\n", argv[i]);
- print_usage(argc, argv);
+ if (!strcmp(argv[0], "sleep")) {
+ cmd = CMD_SLEEP;
+ }
+ else if (!strcmp(argv[0], "restart")) {
+ cmd = CMD_RESTART;
+ }
+ else if (!strcmp(argv[0], "shutdown")) {
+ cmd = CMD_SHUTDOWN;
+ }
+ else if (!strcmp(argv[0], "diagnostics")) {
+ cmd = CMD_DIAGNOSTICS;
+ /* read type */
+ if (!argv[1] || ((strcmp(argv[1], "All") != 0) && (strcmp(argv[1], "WiFi") != 0) && (strcmp(argv[1], "GasGauge") != 0) && (strcmp(argv[1], "NAND") != 0) && (strcmp(argv[1], "HDMI") != 0))) {
+ if (argv[1] == NULL) {
+ cmd_arg = strdup("All");
+ } else {
+ fprintf(stderr, "ERROR: Unknown TYPE %s\n", argv[1]);
+ print_usage(argc+optind, argv-optind, 1);
goto cleanup;
}
-
- cmd_arg = strdup(argv[i]);
- continue;
}
- else if (!strcmp(argv[i], "mobilegestalt")) {
- cmd = CMD_MOBILEGESTALT;
- /* read keys */
- i++;
-
- if (!argv[i] || argv[i] == NULL || (!strncmp(argv[i], "-", 1))) {
- printf("Please supply the key to query.\n");
- print_usage(argc, argv);
- goto cleanup;
- }
-
- keys = plist_new_array();
- while(1) {
- if (argv[i] && (strlen(argv[i]) >= 2) && (strncmp(argv[i], "-", 1) != 0)) {
- plist_array_append_item(keys, plist_new_string(argv[i]));
- i++;
- } else {
- i--;
- break;
- }
- }
- continue;
+ cmd_arg = strdup(argv[1]);
+ }
+ else if (!strcmp(argv[0], "mobilegestalt")) {
+ cmd = CMD_MOBILEGESTALT;
+ /* read keys */
+ if (!argv[1] || !*argv[1]) {
+ fprintf(stderr, "ERROR: Please supply the key to query.\n");
+ print_usage(argc, argv, 1);
+ goto cleanup;
}
- else if (!strcmp(argv[i], "ioreg")) {
- cmd = CMD_IOREGISTRY;
- /* read plane */
+ int i = 1;
+ keys = plist_new_array();
+ while (argv[i] && *argv[i]) {
+ plist_array_append_item(keys, plist_new_string(argv[i]));
i++;
- if (argv[i]) {
- cmd_arg = strdup(argv[i]);
- }
- continue;
}
- else if (!strcmp(argv[i], "ioregentry")) {
- cmd = CMD_IOREGISTRY_ENTRY;
- /* read key */
- i++;
- if (argv[i]) {
- cmd_arg = strdup(argv[i]);
- }
- continue;
+ }
+ else if (!strcmp(argv[0], "ioreg")) {
+ cmd = CMD_IOREGISTRY;
+ /* read plane */
+ if (argv[1]) {
+ cmd_arg = strdup(argv[1]);
}
- else {
- print_usage(argc, argv);
- return 0;
+ }
+ else if (!strcmp(argv[0], "ioregentry")) {
+ cmd = CMD_IOREGISTRY_ENTRY;
+ /* read key */
+ if (argv[1]) {
+ cmd_arg = strdup(argv[1]);
}
}
/* verify options */
if (cmd == CMD_NONE) {
- print_usage(argc, argv);
+ fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[0]);
+ print_usage(argc+optind, argv-optind, 1);
goto cleanup;
}
- if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
+ 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, is it plugged in?\n", udid);
+ printf("No device found with udid %s.\n", udid);
} else {
- printf("No device found, is it plugged in?\n");
+ printf("No device found.\n");
}
goto cleanup;
}
- if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &lockdown_client, "idevicediagnostics"))) {
+ if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &lockdown_client, TOOL_NAME))) {
idevice_free(device);
printf("ERROR: Could not connect to lockdownd, error code %d\n", ret);
goto cleanup;
@@ -201,17 +230,23 @@ int main(int argc, char **argv)
/* attempt to use newer diagnostics service available on iOS 5 and later */
ret = lockdownd_start_service(lockdown_client, "com.apple.mobile.diagnostics_relay", &service);
- if (ret != LOCKDOWN_E_SUCCESS) {
+ if (ret == LOCKDOWN_E_INVALID_SERVICE) {
/* attempt to use older diagnostics service */
ret = lockdownd_start_service(lockdown_client, "com.apple.iosdiagnostics.relay", &service);
}
-
lockdownd_client_free(lockdown_client);
+ if (ret != LOCKDOWN_E_SUCCESS) {
+ idevice_free(device);
+ printf("ERROR: Could not start diagnostics relay service: %s\n", lockdownd_strerror(ret));
+ goto cleanup;
+ }
+
+ result = EXIT_FAILURE;
+
if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) {
if (diagnostics_relay_client_new(device, service, &diagnostics_client) != DIAGNOSTICS_RELAY_E_SUCCESS) {
- printf("Could not connect to diagnostics_relay!\n");
- result = -1;
+ printf("ERROR: Could not connect to diagnostics_relay!\n");
} else {
switch (cmd) {
case CMD_SLEEP:
@@ -219,7 +254,7 @@ int main(int argc, char **argv)
printf("Putting device into deep sleep mode.\n");
result = EXIT_SUCCESS;
} else {
- printf("Failed to put device into deep sleep mode.\n");
+ printf("ERROR: Failed to put device into deep sleep mode.\n");
}
break;
case CMD_RESTART:
@@ -227,7 +262,7 @@ int main(int argc, char **argv)
printf("Restarting device.\n");
result = EXIT_SUCCESS;
} else {
- printf("Failed to restart device.\n");
+ printf("ERROR: Failed to restart device.\n");
}
break;
case CMD_SHUTDOWN:
@@ -235,7 +270,7 @@ int main(int argc, char **argv)
printf("Shutting down device.\n");
result = EXIT_SUCCESS;
} else {
- printf("Failed to shutdown device.\n");
+ printf("ERROR: Failed to shutdown device.\n");
}
break;
case CMD_MOBILEGESTALT:
@@ -245,7 +280,7 @@ int main(int argc, char **argv)
result = EXIT_SUCCESS;
}
} else {
- printf("Unable to query mobilegestalt keys.\n");
+ printf("ERROR: Unable to query mobilegestalt keys.\n");
}
break;
case CMD_IOREGISTRY_ENTRY:
@@ -255,7 +290,7 @@ int main(int argc, char **argv)
result = EXIT_SUCCESS;
}
} else {
- printf("Unable to retrieve IORegistry from device.\n");
+ printf("ERROR: Unable to retrieve IORegistry from device.\n");
}
break;
case CMD_IOREGISTRY:
@@ -265,7 +300,7 @@ int main(int argc, char **argv)
result = EXIT_SUCCESS;
}
} else {
- printf("Unable to retrieve IORegistry from device.\n");
+ printf("ERROR: Unable to retrieve IORegistry from device.\n");
}
break;
case CMD_DIAGNOSTICS:
@@ -276,7 +311,7 @@ int main(int argc, char **argv)
result = EXIT_SUCCESS;
}
} else {
- printf("Unable to retrieve diagnostics from device.\n");
+ printf("ERROR: Unable to retrieve diagnostics from device.\n");
}
break;
}
@@ -285,7 +320,7 @@ int main(int argc, char **argv)
diagnostics_relay_client_free(diagnostics_client);
}
} else {
- printf("Could not start diagnostics service!\n");
+ printf("ERROR: Could not start diagnostics service!\n");
}
if (service) {
@@ -307,25 +342,3 @@ cleanup:
}
return result;
}
-
-void print_usage(int argc, char **argv)
-{
- char *name = NULL;
- name = strrchr(argv[0], '/');
- printf("Usage: %s COMMAND [OPTIONS]\n", (name ? name + 1: argv[0]));
- printf("Use diagnostics interface of a device running iOS 4 or later.\n\n");
- printf(" Where COMMAND is one of:\n");
- printf(" diagnostics [TYPE]\t\tprint diagnostics information from device by TYPE (All, WiFi, GasGauge, NAND)\n");
- printf(" mobilegestalt KEY [...]\tprint mobilegestalt keys passed as arguments separated by a space.\n");
- printf(" ioreg [PLANE]\t\t\tprint IORegistry of device, optionally by PLANE (IODeviceTree, IOPower, IOService) (iOS 5+ only)\n");
- printf(" ioregentry [KEY]\t\tprint IORegistry entry of device (AppleARMPMUCharger, ASPStorage, ...) (iOS 5+ only)\n");
- printf(" shutdown\t\t\tshutdown device\n");
- printf(" restart\t\t\trestart device\n");
- printf(" sleep\t\t\t\tput device into sleep mode (disconnects from host)\n\n");
- printf(" The following OPTIONS are accepted:\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
-}
diff --git a/tools/ideviceenterrecovery.c b/tools/ideviceenterrecovery.c
index ec9093b..29cc5c9 100644
--- a/tools/ideviceenterrecovery.c
+++ b/tools/ideviceenterrecovery.c
@@ -23,10 +23,13 @@
#include <config.h>
#endif
+#define TOOL_NAME "ideviceenterrecovery"
+
#include <stdio.h>
#include <string.h>
-#include <errno.h>
#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
#ifndef WIN32
#include <signal.h>
#endif
@@ -34,17 +37,22 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] UDID\n", (name ? name + 1: argv[0]));
- printf("Makes a device with the supplied UDID enter recovery mode immediately.\n\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] UDID\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Makes a device with the supplied UDID enter recovery mode immediately.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -d, --debug enable communication debugging\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
int main(int argc, char *argv[])
@@ -53,53 +61,79 @@ int main(int argc, char *argv[])
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
idevice_t device = NULL;
idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
- int i;
const char* udid = NULL;
+ int c = 0;
+ const struct option longopts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
+ };
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
+ 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;
- i--;
- if (argc < 2 || !argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return 0;
+ if (!argv[0]) {
+ fprintf(stderr, "ERROR: No UDID specified\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- udid = argv[i];
+ udid = argv[0];
ret = idevice_new(&device, udid);
if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found with udid %s, is it plugged in?\n", udid);
- return -1;
+ printf("No device found with udid %s.\n", udid);
+ return 1;
}
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new(device, &client, "ideviceenterrecovery"))) {
- printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new(device, &client, TOOL_NAME))) {
+ printf("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
idevice_free(device);
- return -1;
+ return 1;
}
- /* run query and output information */
+ int res = 0;
printf("Telling device with udid %s to enter recovery mode.\n", udid);
- if(lockdownd_enter_recovery(client) != LOCKDOWN_E_SUCCESS)
- {
+ ldret = lockdownd_enter_recovery(client);
+ if (ldret == LOCKDOWN_E_SESSION_INACTIVE) {
+ lockdownd_client_free(client);
+ client = NULL;
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
+ printf("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
+ idevice_free(device);
+ return 1;
+ }
+ ldret = lockdownd_enter_recovery(client);
+ }
+ if (ldret != LOCKDOWN_E_SUCCESS) {
printf("Failed to enter recovery mode.\n");
+ res = 1;
+ } else {
+ printf("Device is successfully switching to recovery mode.\n");
}
- printf("Device is successfully switching to recovery mode.\n");
lockdownd_client_free(client);
idevice_free(device);
- return 0;
+ return res;
}
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
index 74df5e4..f551b6c 100644
--- a/tools/ideviceimagemounter.c
+++ b/tools/ideviceimagemounter.c
@@ -23,6 +23,8 @@
#include <config.h>
#endif
+#define TOOL_NAME "ideviceimagemounter"
+
#include <stdlib.h>
#define _GNU_SOURCE 1
#define __USE_GNU 1
@@ -44,12 +46,13 @@
#include <libimobiledevice/notification_proxy.h>
#include <libimobiledevice/mobile_image_mounter.h>
#include <asprintf.h>
-#include "common/utils.h"
+#include <plist/plist.h>
static int list_mode = 0;
+static int use_network = 0;
static int xml_mode = 0;
-static char *udid = NULL;
-static char *imagetype = NULL;
+static const char *udid = NULL;
+static const char *imagetype = NULL;
static const char PKG_PATH[] = "PublicStaging";
static const char PATH_PREFIX[] = "/private/var/mobile/Media";
@@ -59,62 +62,70 @@ typedef enum {
DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
} disk_image_upload_type_t;
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0]));
- printf("Mounts the specified disk image on the device.\n\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -l, --list\t\tList mount information\n");
- printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n");
- printf(" -x, --xml\t\tUse XML output\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ 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,
+ "\n"
+ "Mounts the specified disk image on the device.\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"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
static void parse_opts(int argc, char **argv)
{
static struct option longopts[] = {
- {"help", no_argument, NULL, 'h'},
- {"udid", required_argument, NULL, 'u'},
- {"list", no_argument, NULL, 'l'},
- {"imagetype", required_argument, NULL, 't'},
- {"xml", no_argument, NULL, 'x'},
- {"debug", no_argument, NULL, 'd'},
- {NULL, 0, NULL, 0}
+ { "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' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
};
int c;
while (1) {
- c = getopt_long(argc, argv, "hu:lt:xd", longopts, NULL);
+ c = getopt_long(argc, argv, "hu:lt:xdnv", longopts, NULL);
if (c == -1) {
break;
}
switch (c) {
case 'h':
- print_usage(argc, argv);
+ print_usage(argc, argv, 0);
exit(0);
case 'u':
if (!*optarg) {
fprintf(stderr, "ERROR: UDID must not be empty!\n");
- print_usage(argc, argv);
+ print_usage(argc, argv, 1);
exit(2);
}
- free(udid);
- udid = strdup(optarg);
+ udid = optarg;
+ break;
+ case 'n':
+ use_network = 1;
break;
case 'l':
list_mode = 1;
break;
case 't':
- if (imagetype)
- free(imagetype);
- imagetype = strdup(optarg);
+ imagetype = optarg;
break;
case 'x':
xml_mode = 1;
@@ -122,22 +133,16 @@ static void parse_opts(int argc, char **argv)
case 'd':
idevice_set_debug_level(1);
break;
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ exit(0);
default:
- print_usage(argc, argv);
+ print_usage(argc, argv, 1);
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);
-}
-
static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
{
return fread(buf, 1, size, (FILE*)userdata);
@@ -180,12 +185,16 @@ int main(int argc, char **argv)
}
}
- if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
- printf("No device found, is it plugged in?\n");
+ 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;
}
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter"))) {
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
printf("ERROR: Could not connect to lockdown, error code %d.\n", ldret);
goto leave;
}
@@ -206,6 +215,20 @@ int main(int argc, char **argv)
}
}
+ if (product_version_major == 16) {
+ 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;
+ }
+ }
+
lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &service);
if (!service || service->port == 0) {
@@ -260,17 +283,12 @@ int main(int argc, char **argv)
if (list_mode) {
/* list mounts mode */
if (!imagetype) {
- imagetype = strdup("Developer");
+ imagetype = "Developer";
}
err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
- free(imagetype);
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);
}
@@ -308,7 +326,7 @@ int main(int argc, char **argv)
if (!imagetype) {
- imagetype = strdup("Developer");
+ imagetype = "Developer";
}
switch(disk_image_upload_type) {
@@ -386,7 +404,6 @@ int main(int argc, char **argv)
printf("Mounting...\n");
err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
- free(imagetype);
if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
if (result) {
plist_t node = plist_dict_get_item(result, "Status");
@@ -399,20 +416,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");
@@ -424,19 +433,11 @@ 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);
- }
+ plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
}
}
} else {
diff --git a/tools/ideviceinfo.c b/tools/ideviceinfo.c
index bd3819e..fd45763 100644
--- a/tools/ideviceinfo.c
+++ b/tools/ideviceinfo.c
@@ -24,6 +24,8 @@
#include <config.h>
#endif
+#define TOOL_NAME "ideviceinfo"
+
#include <stdio.h>
#include <string.h>
#include <errno.h>
@@ -35,7 +37,7 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
-#include "common/utils.h"
+#include <plist/plist.h>
#define FORMAT_KEY_VALUE 1
#define FORMAT_XML 2
@@ -72,10 +74,12 @@ static const char *domains[] = {
"com.apple.iTunes",
"com.apple.mobile.iTunes.store",
"com.apple.mobile.iTunes",
+ "com.apple.fmip",
+ "com.apple.Accessibility",
NULL
};
-static int is_domain_known(char *domain)
+static int is_domain_known(const char *domain)
{
int i = 0;
while (domains[i] != NULL) {
@@ -89,28 +93,32 @@ static int is_domain_known(char *domain)
static void print_usage(int argc, char **argv, int is_error)
{
int i = 0;
- char *name = NULL;
- name = strrchr(argv[0], '/');
+ char *name = strrchr(argv[0], '/');
fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
fprintf(is_error ? stderr : stdout,
- "Show information about a connected device.\n\n" \
- " -s, --simple use a simple connection to avoid auto-pairing with the device\n" \
- " -u, --udid UDID target specific device by UDID\n" \
- " -n, --network connect to network device even if available via USB\n" \
- " -q, --domain NAME set domain of query to NAME. Default: None\n" \
- " -k, --key NAME only query key specified by NAME. Default: All keys.\n" \
- " -x, --xml output information as xml plist instead of key/value pairs\n" \
- " -h, --help prints usage information\n" \
- " -d, --debug enable communication debugging\n" \
- "\n"
+ "\n"
+ "Show information about a connected device.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -n, --network connect to network device\n"
+ " -s, --simple use simple connection to avoid auto-pairing with device\n"
+ " -q, --domain NAME set domain of query to NAME. Default: None\n" \
+ " -k, --key NAME only query key specified by NAME. Default: All keys.\n" \
+ " -x, --xml output information in XML property list format\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, " Known domains are:\n\n");
+ fprintf(is_error ? stderr : stdout, "Known domains are:\n\n");
while (domains[i] != NULL) {
fprintf(is_error ? stderr : stdout, " %s\n", domains[i++]);
}
fprintf(is_error ? stderr : stdout,
- "\n" \
- "Homepage: <" PACKAGE_URL ">\n"
+ "\n" \
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
);
}
@@ -122,13 +130,13 @@ int main(int argc, char *argv[])
idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
int simple = 0;
int format = FORMAT_KEY_VALUE;
- char* udid = NULL;
- char *domain = NULL;
- char *key = NULL;
+ const char* udid = NULL;
+ int use_network = 0;
+ const char *domain = NULL;
+ const char *key = NULL;
char *xml_doc = NULL;
uint32_t xml_length;
plist_t node = NULL;
- enum idevice_options lookup_opts = IDEVICE_LOOKUP_USBMUX | IDEVICE_LOOKUP_NETWORK;
int c = 0;
const struct option longopts[] = {
@@ -138,7 +146,9 @@ int main(int argc, char *argv[])
{ "network", no_argument, NULL, 'n' },
{ "domain", required_argument, NULL, 'q' },
{ "key", required_argument, NULL, 'k' },
+ { "simple", no_argument, NULL, 's' },
{ "xml", no_argument, NULL, 'x' },
+ { "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0}
};
@@ -146,7 +156,7 @@ int main(int argc, char *argv[])
signal(SIGPIPE, SIG_IGN);
#endif
- while ((c = getopt_long(argc, argv, "dhu:nq:k:x", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "dhu:nq:k:sxv", longopts, NULL)) != -1) {
switch (c) {
case 'd':
idevice_set_debug_level(1);
@@ -157,11 +167,10 @@ int main(int argc, char *argv[])
print_usage(argc, argv, 1);
return 2;
}
- free(udid);
- udid = strdup(optarg);
+ udid = optarg;
break;
case 'n':
- lookup_opts |= IDEVICE_LOOKUP_PREFER_NETWORK;
+ use_network = 1;
break;
case 'q':
if (!*optarg) {
@@ -169,8 +178,7 @@ int main(int argc, char *argv[])
print_usage(argc, argv, 1);
return 2;
}
- free(domain);
- domain = strdup(optarg);
+ domain = optarg;
break;
case 'k':
if (!*optarg) {
@@ -178,8 +186,7 @@ int main(int argc, char *argv[])
print_usage(argc, argv, 1);
return 2;
}
- free(key);
- key = strdup(optarg);
+ key = optarg;
break;
case 'x':
format = FORMAT_XML;
@@ -190,6 +197,9 @@ int main(int argc, char *argv[])
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;
@@ -199,19 +209,19 @@ int main(int argc, char *argv[])
argc -= optind;
argv += optind;
- ret = idevice_new_with_options(&device, udid, lookup_opts);
+ ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
if (ret != IDEVICE_E_SUCCESS) {
if (udid) {
- printf("ERROR: Device %s not found!\n", udid);
+ fprintf(stderr, "ERROR: Device %s not found!\n", udid);
} else {
- printf("ERROR: No device found!\n");
+ fprintf(stderr, "ERROR: No device found!\n");
}
return -1;
}
if (LOCKDOWN_E_SUCCESS != (ldret = simple ?
- lockdownd_client_new(device, &client, "ideviceinfo"):
- lockdownd_client_new_with_handshake(device, &client, "ideviceinfo"))) {
+ lockdownd_client_new(device, &client, TOOL_NAME):
+ lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
idevice_free(device);
return -1;
@@ -231,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);
@@ -243,8 +253,6 @@ int main(int argc, char *argv[])
}
}
- if (domain != NULL)
- free(domain);
lockdownd_client_free(client);
idevice_free(device);
diff --git a/tools/idevicename.c b/tools/idevicename.c
index a66f5aa..69b76f6 100644
--- a/tools/idevicename.c
+++ b/tools/idevicename.c
@@ -23,6 +23,8 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicename"
+
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -35,55 +37,70 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
-static void print_usage(void)
+static void print_usage(int argc, char** argv, int is_error)
{
- printf("Usage: idevicename [OPTIONS] [NAME]\n");
- printf("Display the device name or set it to NAME if specified.\n");
- printf("\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -h, --help\t\tprint usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [NAME]\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Display the device name or set it to NAME if specified.\n"
+ "\n"
+ "OPTIONS:\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"
+ );
}
int main(int argc, char** argv)
{
- int res = -1;
- char* udid = NULL;
-
int c = 0;
- int optidx = 0;
const struct option longopts[] = {
- { "udid", required_argument, NULL, 'u' },
- { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+ { "network", no_argument, NULL, 'n' },
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0}
};
+ int res = -1;
+ const char* udid = NULL;
+ int use_network = 0;
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
- while ((c = getopt_long(argc, argv, "du:h", longopts, &optidx)) != -1) {
+ while ((c = getopt_long(argc, argv, "du:hnv", longopts, NULL)) != -1) {
switch (c) {
case 'u':
if (!*optarg) {
fprintf(stderr, "ERROR: UDID must not be empty!\n");
- print_usage();
+ print_usage(argc, argv, 1);
exit(2);
}
- free(udid);
- udid = strdup(optarg);
+ udid = optarg;
+ break;
+ case 'n':
+ use_network = 1;
break;
case 'h':
- print_usage();
+ print_usage(argc, argv, 0);
return 0;
case 'd':
idevice_set_debug_level(1);
break;
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ return 0;
default:
- print_usage();
- return -1;
+ print_usage(argc, argv, 1);
+ return 2;
}
}
@@ -91,18 +108,22 @@ int main(int argc, char** argv)
argv += optind;
if (argc > 1) {
- print_usage();
- return -1;
+ print_usage(argc, argv, 1);
+ return 2;
}
idevice_t device = NULL;
- if (idevice_new(&device, udid) != IDEVICE_E_SUCCESS) {
- fprintf(stderr, "ERROR: Could not connect to device\n");
+ if (idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS) {
+ if (udid) {
+ fprintf(stderr, "ERROR: No device found with udid %s.\n", udid);
+ } else {
+ fprintf(stderr, "ERROR: No device found.\n");
+ }
return -1;
}
lockdownd_client_t lockdown = NULL;
- lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, "idevicename");
+ lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
if (lerr != LOCKDOWN_E_SUCCESS) {
idevice_free(device);
fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lerr);
@@ -134,9 +155,5 @@ int main(int argc, char** argv)
lockdownd_client_free(lockdown);
idevice_free(device);
- if (udid) {
- free(udid);
- }
-
return res;
}
diff --git a/tools/idevicenotificationproxy.c b/tools/idevicenotificationproxy.c
index 72b406c..d1e25c1 100644
--- a/tools/idevicenotificationproxy.c
+++ b/tools/idevicenotificationproxy.c
@@ -23,11 +23,14 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicenotificationproxy"
+
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
#include <errno.h>
#include <signal.h>
-#include <stdlib.h>
#ifdef WIN32
#include <windows.h>
@@ -57,23 +60,29 @@ static void clean_exit(int sig)
quit_flag++;
}
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
- printf("Post or observe notifications on a device.\n\n");
- printf(" Where COMMAND is one of:\n");
- printf(" post ID [...]\t\tpost notification IDs to device and exit\n");
- printf(" observe ID [...]\tobserve notification IDs in the foreground until CTRL+C or signal is received\n");
- printf("\n");
- printf(" The following OPTIONS are accepted:\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ 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"
+ "Post or observe notifications on a device.\n"
+ "\n"
+ "Where COMMAND is one of:\n"
+ " post ID [...] post notification IDs to device and exit\n"
+ " observe ID [...] observe notification IDs in foreground until CTRL+C\n"
+ " or signal is received\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 prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
static void notify_cb(const char *notification, void *user_data)
@@ -90,8 +99,9 @@ int main(int argc, char *argv[])
np_client_t gnp = NULL;
int result = -1;
- int i;
+ int i = 0;
const char* udid = NULL;
+ int use_network = 0;
int cmd = CMD_NONE;
char* cmd_arg = NULL;
@@ -99,6 +109,16 @@ int main(int argc, char *argv[])
char **nspec = NULL;
char **nspectmp = 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}
+ };
+
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
#ifndef WIN32
@@ -107,83 +127,94 @@ int main(int argc, char *argv[])
#endif
/* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- result = 0;
- goto cleanup;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = argv[i];
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
- result = 0;
- goto cleanup;
+ 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;
}
- else if (!strcmp(argv[i], "post") || !strcmp(argv[i], "observe")) {
- cmd = CMD_POST;
- if (!strcmp(argv[i], "observe")) {
- cmd = CMD_OBSERVE;
- }
-
- i++;
+ }
+ argc -= optind;
+ argv += optind;
- if (!argv[i] || argv[i] == NULL || (!strncmp(argv[i], "-", 1))) {
- printf("Please supply a valid notification identifier.\n");
- print_usage(argc, argv);
- goto cleanup;
- }
+ if (!argv[i]) {
+ fprintf(stderr, "ERROR: Missing command\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
- count = 0;
- nspec = malloc(sizeof(char*) * (count+1));
-
- while(1) {
- if (argv[i] && (strlen(argv[i]) >= 2) && (strncmp(argv[i], "-", 1) != 0)) {
- nspectmp = realloc(nspec, sizeof(char*) * (count+1));
- nspectmp[count] = strdup(argv[i]);
- nspec = nspectmp;
- count = count+1;
- i++;
- } else {
- i--;
- break;
- }
- }
+ if (!strcmp(argv[i], "post")) {
+ cmd = CMD_POST;
+ } else if (!strcmp(argv[i], "observe")) {
+ cmd = CMD_OBSERVE;
+ }
- nspectmp = realloc(nspec, sizeof(char*) * (count+1));
- nspectmp[count] = NULL;
- nspec = nspectmp;
- continue;
+ if (cmd == CMD_POST || cmd == CMD_OBSERVE) {
+ i++;
+ if (!argv[i]) {
+ fprintf(stderr, "ERROR: Please supply a valid notification identifier.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else {
- print_usage(argc, argv);
- return 0;
+
+ count = 0;
+ nspec = malloc(sizeof(char*) * (count+1));
+
+ while(1) {
+ if (argv[i] && (strlen(argv[i]) >= 2) && (strncmp(argv[i], "-", 1) != 0)) {
+ nspectmp = realloc(nspec, sizeof(char*) * (count+1));
+ nspectmp[count] = strdup(argv[i]);
+ nspec = nspectmp;
+ count = count+1;
+ i++;
+ } else {
+ i--;
+ break;
+ }
}
+
+ nspectmp = realloc(nspec, sizeof(char*) * (count+1));
+ nspectmp[count] = NULL;
+ nspec = nspectmp;
}
/* verify options */
if (cmd == CMD_NONE) {
- print_usage(argc, argv);
- goto cleanup;
+ fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[0]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
+ 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, is it plugged in?\n", udid);
+ printf("No device found with udid %s.\n", udid);
} else {
- printf("No device found, is it plugged in?\n");
+ printf("No device found.\n");
}
goto cleanup;
}
- if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &client, "idevicenotificationproxy"))) {
+ 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);
goto cleanup;
}
@@ -233,7 +264,7 @@ int main(int argc, char *argv[])
}
}
} else {
- printf("Could not start notification_proxy service on device.\n");
+ printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ret));
}
if (service) {
diff --git a/tools/idevicepair.c b/tools/idevicepair.c
index bc0f5d8..94d3f04 100644
--- a/tools/idevicepair.c
+++ b/tools/idevicepair.c
@@ -2,7 +2,7 @@
* idevicepair.c
* Manage pairings with devices and this host
*
- * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2010-2021 Nikias Bassen, All Rights Reserved.
* Copyright (c) 2014 Martin Szulecki, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
@@ -24,20 +24,93 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicepair"
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
-#ifndef WIN32
+#include <ctype.h>
+#include <unistd.h>
+#ifdef WIN32
+#include <windows.h>
+#include <conio.h>
+#else
+#include <termios.h>
#include <signal.h>
#endif
+
#include "common/userpref.h"
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
+#include <plist/plist.h>
static char *udid = NULL;
+#ifdef HAVE_WIRELESS_PAIRING
+
+#ifdef WIN32
+#define BS_CC '\b'
+#define my_getch getch
+#else
+#define BS_CC 0x7f
+static int my_getch(void)
+{
+ struct termios oldt, newt;
+ int ch;
+ tcgetattr(STDIN_FILENO, &oldt);
+ newt = oldt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+ ch = getchar();
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+ return ch;
+}
+#endif
+
+static int get_hidden_input(char *buf, int maxlen)
+{
+ int pwlen = 0;
+ int c;
+
+ while ((c = my_getch())) {
+ if ((c == '\r') || (c == '\n')) {
+ break;
+ } else if (isprint(c)) {
+ if (pwlen < maxlen-1)
+ buf[pwlen++] = c;
+ fputc('*', stderr);
+ } else if (c == BS_CC) {
+ if (pwlen > 0) {
+ fputs("\b \b", stderr);
+ pwlen--;
+ }
+ }
+ }
+ buf[pwlen] = 0;
+ return pwlen;
+}
+
+static void pairing_cb(lockdownd_cu_pairing_cb_type_t cb_type, void *user_data, void* data_ptr, unsigned int* data_size)
+{
+ if (cb_type == LOCKDOWN_CU_PAIRING_PIN_REQUESTED) {
+ printf("Enter PIN: ");
+ fflush(stdout);
+
+ *data_size = get_hidden_input((char*)data_ptr, *data_size);
+
+ printf("\n");
+ } else if (cb_type == LOCKDOWN_CU_PAIRING_DEVICE_INFO) {
+ printf("Device info:\n");
+ 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)");
+ }
+}
+
+#endif /* HAVE_WIRELESS_PAIRING */
+
static void print_error_message(lockdownd_error_t err)
{
switch (err) {
@@ -54,98 +127,188 @@ static void print_error_message(lockdownd_error_t err)
case LOCKDOWN_E_USER_DENIED_PAIRING:
printf("ERROR: Device %s said that the user denied the trust dialog.\n", udid);
break;
+ case LOCKDOWN_E_PAIRING_FAILED:
+ printf("ERROR: Pairing with device %s failed.\n", udid);
+ break;
+ case LOCKDOWN_E_GET_PROHIBITED:
+ case LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION:
+ printf("ERROR: Pairing is not possible over this connection.\n");
+#ifdef HAVE_WIRELESS_PAIRING
+ printf("To perform a wireless pairing use the -w command line switch. See usage or man page for details.\n");
+#endif
+ break;
default:
printf("ERROR: Device %s returned unhandled error code %d\n", udid, err);
break;
}
}
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("\n%s - Manage host pairings with devices and usbmuxd.\n\n", (name ? name + 1: argv[0]));
- printf("Usage: %s [OPTIONS] COMMAND\n\n", (name ? name + 1: argv[0]));
- printf(" Where COMMAND is one of:\n");
- printf(" systembuid print the system buid of the usbmuxd host\n");
- printf(" hostid print the host id for target device\n");
- printf(" pair pair device with this host\n");
- printf(" validate validate if device is paired with this host\n");
- printf(" unpair unpair device with this host\n");
- printf(" list list devices paired with this host\n\n");
- printf(" The following OPTIONS are accepted:\n");
- printf(" -d, --debug enable communication debugging\n");
- printf(" -u, --udid UDID target specific device by UDID\n");
- printf(" -h, --help prints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ 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"
+ "Manage host pairings with devices and usbmuxd.\n"
+ "\n"
+ "Where COMMAND is one of:\n"
+ " systembuid print the system buid of the usbmuxd host\n"
+ " hostid print the host id for target device\n"
+ " pair pair device with this host\n"
+ " validate validate if device is paired with this host\n"
+ " unpair unpair device with this host\n"
+ " list list devices paired with this host\n"
+ "\n"
+ "The following OPTIONS are accepted:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ );
+#ifdef HAVE_WIRELESS_PAIRING
+ fprintf(is_error ? stderr : stdout,
+ " -w, --wireless perform wireless pairing (see NOTE)\n"
+ " -n, --network connect to network device (see NOTE)\n"
+ );
+#endif
+ fprintf(is_error ? stderr : stdout,
+ " -d, --debug enable communication debugging\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ );
+#ifdef HAVE_WIRELESS_PAIRING
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "NOTE: Pairing over network (wireless pairing) is only supported by Apple TV\n"
+ "devices. To perform a wireless pairing, you need to use the -w command line\n"
+ "switch. Make sure to put the device into pairing mode first by opening\n"
+ "Settings > Remotes and Devices > Remote App and Devices.\n"
+ );
+#endif
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
-static void parse_opts(int argc, char **argv)
+int main(int argc, char **argv)
{
+ int c = 0;
static struct option longopts[] = {
- {"help", no_argument, NULL, 'h'},
- {"udid", required_argument, NULL, 'u'},
- {"debug", no_argument, NULL, 'd'},
- {NULL, 0, NULL, 0}
+ { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+#ifdef HAVE_WIRELESS_PAIRING
+ { "wireless", no_argument, NULL, 'w' },
+ { "network", no_argument, NULL, 'n' },
+ { "hostinfo", required_argument, NULL, 1 },
+#endif
+ { "debug", no_argument, NULL, 'd' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
};
- int c;
+#ifdef HAVE_WIRELESS_PAIRING
+#define SHORT_OPTIONS "hu:wndv"
+#else
+#define SHORT_OPTIONS "hu:dv"
+#endif
+ lockdownd_client_t client = NULL;
+ idevice_t device = NULL;
+ idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
+ lockdownd_error_t lerr;
+ int result;
- while (1) {
- c = getopt_long(argc, argv, "hu:d", longopts, (int*)0);
- if (c == -1) {
- break;
- }
+ char *type = NULL;
+ int use_network = 0;
+ int wireless_pairing = 0;
+#ifdef HAVE_WIRELESS_PAIRING
+ plist_t host_info_plist = NULL;
+#endif
+ char *cmd;
+ typedef enum {
+ OP_NONE = 0, OP_PAIR, OP_VALIDATE, OP_UNPAIR, OP_LIST, OP_HOSTID, OP_SYSTEMBUID
+ } op_t;
+ op_t op = OP_NONE;
+ while ((c = getopt_long(argc, argv, SHORT_OPTIONS, longopts, NULL)) != -1) {
switch (c) {
case 'h':
- print_usage(argc, argv);
+ print_usage(argc, argv, 0);
exit(EXIT_SUCCESS);
case 'u':
if (!*optarg) {
fprintf(stderr, "ERROR: UDID must not be empty!\n");
- print_usage(argc, argv);
- exit(2);
+ print_usage(argc, argv, 1);
+ result = EXIT_FAILURE;
+ goto leave;
}
- if (udid)
- free(udid);
+ free(udid);
udid = strdup(optarg);
break;
+#ifdef HAVE_WIRELESS_PAIRING
+ case 'w':
+ wireless_pairing = 1;
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 1:
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: --hostinfo argument must not be empty!\n");
+ result = EXIT_FAILURE;
+ goto leave;
+ }
+ if (*optarg == '@') {
+ 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;
+ goto leave;
+ }
+ }
+#ifdef HAVE_PLIST_JSON
+ else if (*optarg == '{') {
+ if (plist_from_json(optarg, strlen(optarg), &host_info_plist) != PLIST_ERR_SUCCESS) {
+ fprintf(stderr, "ERROR: --hostinfo argument not valid. Make sure it is a JSON dictionary.\n");
+ result = EXIT_FAILURE;
+ goto leave;
+ }
+ }
+#endif
+ else {
+ fprintf(stderr, "ERROR: --hostinfo argument not valid. To specify a path prefix with '@'\n");
+ result = EXIT_FAILURE;
+ goto leave;
+ }
+ break;
+#endif
case 'd':
idevice_set_debug_level(1);
break;
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ result = EXIT_SUCCESS;
+ goto leave;
default:
- print_usage(argc, argv);
- exit(EXIT_SUCCESS);
+ print_usage(argc, argv, 1);
+ result = EXIT_FAILURE;
+ goto leave;
}
}
-}
-
-int main(int argc, char **argv)
-{
- lockdownd_client_t client = NULL;
- idevice_t device = NULL;
- idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
- lockdownd_error_t lerr;
- int result;
-
- char *type = NULL;
- char *cmd;
- typedef enum {
- OP_NONE = 0, OP_PAIR, OP_VALIDATE, OP_UNPAIR, OP_LIST, OP_HOSTID, OP_SYSTEMBUID
- } op_t;
- op_t op = OP_NONE;
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
- parse_opts(argc, argv);
if ((argc - optind) < 1) {
- printf("ERROR: You need to specify a COMMAND!\n");
- print_usage(argc, argv);
- exit(EXIT_FAILURE);
+ fprintf(stderr, "ERROR: You need to specify a COMMAND!\n");
+ print_usage(argc, argv, 1);
+ result = EXIT_FAILURE;
+ goto leave;
+ }
+
+ if (wireless_pairing && use_network) {
+ fprintf(stderr, "ERROR: You cannot use -w and -n together.\n");
+ print_usage(argc, argv, 1);
+ result = EXIT_FAILURE;
+ goto leave;
}
cmd = (argv+optind)[0];
@@ -163,9 +326,20 @@ int main(int argc, char **argv)
} else if (!strcmp(cmd, "systembuid")) {
op = OP_SYSTEMBUID;
} else {
- printf("ERROR: Invalid command '%s' specified\n", cmd);
- print_usage(argc, argv);
- exit(EXIT_FAILURE);
+ fprintf(stderr, "ERROR: Invalid command '%s' specified\n", cmd);
+ print_usage(argc, argv, 1);
+ result = EXIT_FAILURE;
+ goto leave;
+ }
+
+ if (wireless_pairing) {
+ if (op == OP_VALIDATE || op == OP_UNPAIR) {
+ fprintf(stderr, "ERROR: Command '%s' is not supported with -w\n", cmd);
+ print_usage(argc, argv, 1);
+ result = EXIT_FAILURE;
+ goto leave;
+ }
+ use_network = 1;
}
if (op == OP_SYSTEMBUID) {
@@ -174,10 +348,10 @@ int main(int argc, char **argv)
printf("%s\n", systembuid);
- if (systembuid)
- free(systembuid);
+ free(systembuid);
- return EXIT_SUCCESS;
+ result = EXIT_SUCCESS;
+ goto leave;
}
if (op == OP_LIST) {
@@ -189,36 +363,29 @@ int main(int argc, char **argv)
printf("%s\n", udids[i]);
free(udids[i]);
}
- if (udids)
- free(udids);
- if (udid)
- free(udid);
- return EXIT_SUCCESS;
- }
-
- if (udid) {
- ret = idevice_new(&device, udid);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found with udid %s, is it plugged in?\n", udid);
- free(udid);
- return EXIT_FAILURE;
- }
- free(udid);
- udid = NULL;
- } else {
- ret = idevice_new(&device, NULL);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found, is it plugged in?\n");
- return EXIT_FAILURE;
- }
+ free(udids);
+ result = EXIT_SUCCESS;
+ goto leave;
}
- ret = idevice_get_udid(device, &udid);
+ ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
if (ret != IDEVICE_E_SUCCESS) {
- printf("ERROR: Could not get device udid, error code %d\n", ret);
+ if (udid) {
+ printf("No device found with udid %s.\n", udid);
+ } else {
+ printf("No device found.\n");
+ }
result = EXIT_FAILURE;
goto leave;
}
+ if (!udid) {
+ ret = idevice_get_udid(device, &udid);
+ if (ret != IDEVICE_E_SUCCESS) {
+ printf("ERROR: Could not get device udid, error code %d\n", ret);
+ result = EXIT_FAILURE;
+ goto leave;
+ }
+ }
if (op == OP_HOSTID) {
plist_t pair_record = NULL;
@@ -229,20 +396,18 @@ int main(int argc, char **argv)
printf("%s\n", hostid);
- if (hostid)
- free(hostid);
+ free(hostid);
+ plist_free(pair_record);
- if (pair_record)
- plist_free(pair_record);
-
- return EXIT_SUCCESS;
+ result = EXIT_SUCCESS;
+ goto leave;
}
- lerr = lockdownd_client_new(device, &client, "idevicepair");
+ lerr = lockdownd_client_new(device, &client, TOOL_NAME);
if (lerr != LOCKDOWN_E_SUCCESS) {
- idevice_free(device);
printf("ERROR: Could not connect to lockdownd, error code %d\n", lerr);
- return EXIT_FAILURE;
+ result = EXIT_FAILURE;
+ goto leave;
}
result = EXIT_SUCCESS;
@@ -253,18 +418,26 @@ int main(int argc, char **argv)
result = EXIT_FAILURE;
goto leave;
} else {
- if (strcmp("com.apple.mobile.lockdown", type)) {
+ if (strcmp("com.apple.mobile.lockdown", type) != 0) {
printf("WARNING: QueryType request returned '%s'\n", type);
}
- if (type) {
- free(type);
- }
+ free(type);
}
switch(op) {
default:
case OP_PAIR:
- lerr = lockdownd_pair(client, NULL);
+#ifdef HAVE_WIRELESS_PAIRING
+ if (wireless_pairing) {
+ lerr = lockdownd_cu_pairing_create(client, pairing_cb, NULL, host_info_plist, NULL);
+ if (lerr == LOCKDOWN_E_SUCCESS) {
+ lerr = lockdownd_pair_cu(client);
+ }
+ } else
+#endif
+ {
+ lerr = lockdownd_pair(client, NULL);
+ }
if (lerr == LOCKDOWN_E_SUCCESS) {
printf("SUCCESS: Paired with device %s\n", udid);
} else {
@@ -276,7 +449,7 @@ int main(int argc, char **argv)
case OP_VALIDATE:
lockdownd_client_free(client);
client = NULL;
- lerr = lockdownd_client_new_with_handshake(device, &client, "idevicepair");
+ lerr = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME);
if (lerr == LOCKDOWN_E_SUCCESS) {
printf("SUCCESS: Validated pairing with device %s\n", udid);
} else {
@@ -299,9 +472,8 @@ int main(int argc, char **argv)
leave:
lockdownd_client_free(client);
idevice_free(device);
- if (udid) {
- free(udid);
- }
+ free(udid);
+
return result;
}
diff --git a/tools/ideviceprovision.c b/tools/ideviceprovision.c
index 7e8367a..4080a28 100644
--- a/tools/ideviceprovision.c
+++ b/tools/ideviceprovision.c
@@ -24,9 +24,12 @@
#include <config.h>
#endif
+#define TOOL_NAME "ideviceprovision"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
#include <sys/stat.h>
#include <errno.h>
#ifndef WIN32
@@ -42,36 +45,42 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/misagent.h>
-#include "common/utils.h"
+#include <plist/plist.h>
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
- printf("Manage provisioning profiles on a device.\n\n");
- printf(" Where COMMAND is one of:\n");
- printf(" install FILE\tInstalls the provisioning profile specified by FILE.\n");
- printf(" \tA valid .mobileprovision file is expected.\n");
- printf(" list\t\tGet a list of all provisioning profiles on the device.\n");
- printf(" copy PATH\tRetrieves all provisioning profiles from the device and\n");
- printf(" \tstores them into the existing directory specified by PATH.\n");
- printf(" \tThe files will be stored as UUID.mobileprovision\n");
- printf(" copy UUID PATH Retrieves the provisioning profile identified by UUID\n");
- printf(" \tfrom the device and stores it into the existing directory\n");
- printf(" \tspecified by PATH. The file will be stored as UUID.mobileprovision.\n");
- printf(" remove UUID\tRemoves the provisioning profile identified by UUID.\n");
- printf(" remove-all\tRemoves all installed provisioning profiles.\n");
- printf(" dump FILE\tPrints detailed information about the provisioning profile\n");
- printf(" \tspecified by FILE.\n\n");
- printf(" The following OPTIONS are accepted:\n");
- printf(" -d, --debug enable communication debugging\n");
- printf(" -u, --udid UDID target specific device by UDID\n");
- printf(" -x, --xml print XML output when using the 'dump' command\n");
- printf(" -h, --help prints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
+ 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"
+ "Manage provisioning profiles on a device.\n"
+ "\n"
+ "Where COMMAND is one of:\n"
+ " install FILE Installs the provisioning profile specified by FILE.\n"
+ " A valid .mobileprovision file is expected.\n"
+ " list Get a list of all provisioning profiles on the device.\n"
+ " copy PATH Retrieves all provisioning profiles from the device and\n"
+ " stores them into the existing directory specified by PATH.\n"
+ " The files will be stored as UUID.mobileprovision\n"
+ " copy UUID PATH Retrieves the provisioning profile identified by UUID\n"
+ " from the device and stores it into the existing directory\n"
+ " specified by PATH. The file will be stored as UUID.mobileprovision.\n"
+ " remove UUID Removes the provisioning profile identified by UUID.\n"
+ " remove-all Removes all installed provisioning profiles.\n"
+ " dump FILE Prints detailed information about the provisioning profile\n"
+ " specified by FILE.\n"
+ "\n"
+ "The following OPTIONS are accepted:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -n, --network connect to network device\n"
+ " -x, --xml print XML output when using the 'dump' command\n"
+ " -d, --debug enable communication debugging\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
enum {
@@ -98,7 +107,7 @@ static void asn1_next_item(unsigned char** p)
}
}
-static size_t asn1_item_get_size(unsigned char* p)
+static size_t asn1_item_get_size(const unsigned char* p)
{
size_t res = 0;
char bsize = *(p+1);
@@ -293,94 +302,116 @@ int main(int argc, char *argv[])
const char* udid = NULL;
const char* param = NULL;
const char* param2 = NULL;
+ int use_network = 0;
+ int c = 0;
+ const struct option longopts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+ { "network", no_argument, NULL, 'n' },
+ { "version", no_argument, NULL, 'v' },
+ { "xml", no_argument, NULL, 'x' },
+ { NULL, 0, NULL, 0}
+ };
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:nvx", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return 0;
- }
- udid = argv[i];
- continue;
- }
- else if (!strcmp(argv[i], "install")) {
- i++;
- if (!argv[i] || (strlen(argv[i]) < 1)) {
- print_usage(argc, argv);
- return 0;
- }
- param = argv[i];
- op = OP_INSTALL;
- continue;
- }
- else if (!strcmp(argv[i], "list")) {
- op = OP_LIST;
- }
- else if (!strcmp(argv[i], "copy")) {
- i++;
- if (!argv[i] || (strlen(argv[i]) < 1)) {
- print_usage(argc, argv);
- return 0;
- }
- param = argv[i];
- op = OP_COPY;
- i++;
- if (argv[i] && (strlen(argv[i]) > 0)) {
- param2 = argv[i];
- }
- continue;
- }
- else if (!strcmp(argv[i], "remove")) {
- i++;
- if (!argv[i] || (strlen(argv[i]) < 1)) {
- print_usage(argc, argv);
- return 0;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- param = argv[i];
- op = OP_REMOVE;
- continue;
+ 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;
+ case 'x':
+ output_xml = 1;
+ break;
+ default:
+ print_usage(argc, argv, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "remove-all")) {
- i++;
- op = OP_REMOVE;
- continue;
+ }
+ 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], "install")) {
+ op = OP_INSTALL;
+ i++;
+ if (!argv[i] || !*argv[i]) {
+ fprintf(stderr, "Missing argument for 'install' command.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "dump")) {
- i++;
- if (!argv[i] || (strlen(argv[i]) < 1)) {
- print_usage(argc, argv);
- return 0;
- }
- param = argv[i];
- op = OP_DUMP;
- continue;
+ param = argv[i];
+ }
+ else if (!strcmp(argv[i], "list")) {
+ op = OP_LIST;
+ }
+ else if (!strcmp(argv[i], "copy")) {
+ op = OP_COPY;
+ i++;
+ if (!argv[i] || !*argv[i]) {
+ fprintf(stderr, "Missing argument for 'copy' command.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "-x") || !strcmp(argv[i], "--xml")) {
- output_xml = 1;
- continue;
+ param = argv[i];
+ i++;
+ if (argv[i] && (strlen(argv[i]) > 0)) {
+ param2 = argv[i];
}
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
- return 0;
+ }
+ else if (!strcmp(argv[i], "remove")) {
+ op = OP_REMOVE;
+ i++;
+ if (!argv[i] || !*argv[i]) {
+ fprintf(stderr, "Missing argument for 'remove' command.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else {
- print_usage(argc, argv);
- return 0;
+ param = argv[i];
+ }
+ else if (!strcmp(argv[i], "remove-all")) {
+ op = OP_REMOVE;
+ }
+ else if (!strcmp(argv[i], "dump")) {
+ op = OP_DUMP;
+ i++;
+ if (!argv[i] || !*argv[i]) {
+ fprintf(stderr, "Missing argument for 'remove' command.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
+ param = argv[i];
}
-
if ((op == -1) || (op >= NUM_OPS)) {
- print_usage(argc, argv);
- return 0;
+ fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[i]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
if (op == OP_DUMP) {
@@ -405,7 +436,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;
@@ -417,7 +448,9 @@ int main(int argc, char *argv[])
plist_free(pl);
return res;
- } else if (op == OP_COPY) {
+ }
+
+ if (op == OP_COPY) {
struct stat st;
const char *checkdir = (param2) ? param2 : param;
if ((stat(checkdir, &st) < 0) || !S_ISDIR(st.st_mode)) {
@@ -426,17 +459,17 @@ int main(int argc, char *argv[])
}
}
- ret = idevice_new(&device, udid);
+ 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, is it plugged in?\n", udid);
+ printf("No device found with udid %s.\n", udid);
} else {
- printf("No device found, is it plugged in?\n");
+ printf("No device found.\n");
}
return -1;
}
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, "ideviceprovision"))) {
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
idevice_free(device);
return -1;
@@ -464,8 +497,9 @@ int main(int argc, char *argv[])
}
int product_version = ((product_version_major & 0xFF) << 16) | ((product_version_minor & 0xFF) << 8) | (product_version_patch & 0xFF);
- if (LOCKDOWN_E_SUCCESS != lockdownd_start_service(client, "com.apple.misagent", &service)) {
- fprintf(stderr, "Could not start service \"com.apple.misagent\"\n");
+ lockdownd_error_t lerr = lockdownd_start_service(client, MISAGENT_SERVICE_NAME, &service);
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ fprintf(stderr, "Could not start service %s: %s\n", MISAGENT_SERVICE_NAME, lockdownd_strerror(lerr));
lockdownd_client_free(client);
idevice_free(device);
return -1;
@@ -475,7 +509,7 @@ int main(int argc, char *argv[])
misagent_client_t mis = NULL;
if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) {
- fprintf(stderr, "Could not connect to \"com.apple.misagent\" on device\n");
+ fprintf(stderr, "Could not connect to %s on device\n", MISAGENT_SERVICE_NAME);
if (service)
lockdownd_service_descriptor_free(service);
lockdownd_client_free(client);
diff --git a/tools/idevicescreenshot.c b/tools/idevicescreenshot.c
index 38d323c..0e694c7 100644
--- a/tools/idevicescreenshot.c
+++ b/tools/idevicescreenshot.c
@@ -23,11 +23,15 @@
#include <config.h>
#endif
+#define TOOL_NAME "idevicescreenshot"
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <getopt.h>
#include <errno.h>
#include <time.h>
+#include <unistd.h>
#ifndef WIN32
#include <signal.h>
#endif
@@ -36,7 +40,86 @@
#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/screenshotr.h>
-void print_usage(int argc, char **argv);
+static void get_image_filename(char *imgdata, char **filename)
+{
+ // If the provided filename already has an extension, use it as is.
+ if (*filename) {
+ char *last_dot = strrchr(*filename, '.');
+ if (last_dot && !strchr(last_dot, '/')) {
+ return;
+ }
+ }
+
+ // Find the appropriate file extension for the filename.
+ const char *fileext = NULL;
+ if (memcmp(imgdata, "\x89PNG", 4) == 0) {
+ fileext = ".png";
+ } else if (memcmp(imgdata, "MM\x00*", 4) == 0) {
+ fileext = ".tiff";
+ } else {
+ printf("WARNING: screenshot data has unexpected image format.\n");
+ fileext = ".dat";
+ }
+
+ // If a filename without an extension is provided, append the extension.
+ // Otherwise, generate a filename based on the current time.
+ char *basename = NULL;
+ if (*filename) {
+ basename = (char*)malloc(strlen(*filename) + 1);
+ strcpy(basename, *filename);
+ free(*filename);
+ *filename = NULL;
+ } else {
+ time_t now = time(NULL);
+ basename = (char*)malloc(32);
+ strftime(basename, 31, "screenshot-%Y-%m-%d-%H-%M-%S", gmtime(&now));
+ }
+
+ // Ensure the filename is unique on disk.
+ char *unique_filename = (char*)malloc(strlen(basename) + strlen(fileext) + 7);
+ sprintf(unique_filename, "%s%s", basename, fileext);
+ int i;
+ for (i = 2; i < (1 << 16); i++) {
+ if (access(unique_filename, F_OK) == -1) {
+ *filename = unique_filename;
+ break;
+ }
+ sprintf(unique_filename, "%s-%d%s", basename, i, fileext);
+ }
+ if (!*filename) {
+ free(unique_filename);
+ }
+ free(basename);
+}
+
+static void print_usage(int argc, char **argv, int is_error)
+{
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [FILE]\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Gets a screenshot from a connected device.\n"
+ "\n"
+ "The image is in PNG format for iOS 9+ and otherwise in TIFF format.\n"
+ "The screenshot is saved as an image with the given FILE name.\n"
+ "If FILE has no extension, FILE will be a prefix of the saved filename.\n"
+ "If FILE is not specified, \"screenshot-DATE\", will be used as a prefix\n"
+ "of the filename, e.g.:\n"
+ " ./screenshot-2013-12-31-23-59-59.tiff\n"
+ "\n"
+ "NOTE: A mounted developer disk image is required on the device, otherwise\n"
+ "the screenshotr service is not available.\n"
+ "\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 prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
+}
int main(int argc, char **argv)
{
@@ -46,92 +129,99 @@ int main(int argc, char **argv)
screenshotr_client_t shotr = NULL;
lockdownd_service_descriptor_t service = NULL;
int result = -1;
- int i;
const char *udid = NULL;
+ int use_network = 0;
char *filename = 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 */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+
+ /* parse cmdline arguments */
+ while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return 0;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = argv[i];
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
+ udid = optarg;
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'h':
+ print_usage(argc, argv, 0);
return 0;
- }
- else if (argv[i][0] != '-' && !filename) {
- filename = strdup(argv[i]);
- continue;
- }
- else {
- print_usage(argc, argv);
+ 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]) {
+ filename = strdup(argv[0]);
+ }
- if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
+ 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, is it plugged in?\n", udid);
+ printf("No device found with udid %s.\n", udid);
} else {
- printf("No device found, is it plugged in?\n");
+ printf("No device found.\n");
}
return -1;
}
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, NULL))) {
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
idevice_free(device);
printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
return -1;
}
- lockdownd_start_service(lckd, "com.apple.mobile.screenshotr", &service);
+ lockdownd_error_t lerr = lockdownd_start_service(lckd, SCREENSHOTR_SERVICE_NAME, &service);
lockdownd_client_free(lckd);
- if (service && service->port > 0) {
+ if (lerr == LOCKDOWN_E_SUCCESS) {
if (screenshotr_client_new(device, service, &shotr) != SCREENSHOTR_E_SUCCESS) {
printf("Could not connect to screenshotr!\n");
} else {
char *imgdata = NULL;
uint64_t imgsize = 0;
if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) == SCREENSHOTR_E_SUCCESS) {
+ get_image_filename(imgdata, &filename);
if (!filename) {
- const char *fileext = NULL;
- if (memcmp(imgdata, "\x89PNG", 4) == 0) {
- fileext = ".png";
- } else if (memcmp(imgdata, "MM\x00*", 4) == 0) {
- fileext = ".tiff";
- } else {
- printf("WARNING: screenshot data has unexpected image format.\n");
- fileext = ".dat";
- }
- time_t now = time(NULL);
- filename = (char*)malloc(36);
- size_t pos = strftime(filename, 36, "screenshot-%Y-%m-%d-%H-%M-%S", gmtime(&now));
- sprintf(filename+pos, "%s", fileext);
- }
- FILE *f = fopen(filename, "wb");
- if (f) {
- if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) {
- printf("Screenshot saved to %s\n", filename);
- result = 0;
+ printf("FATAL: Could not find a unique filename!\n");
+ } else {
+ FILE *f = fopen(filename, "wb");
+ if (f) {
+ if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) {
+ printf("Screenshot saved to %s\n", filename);
+ result = 0;
+ } else {
+ printf("Could not save screenshot to file %s!\n", filename);
+ }
+ fclose(f);
} else {
- printf("Could not save screenshot to file %s!\n", filename);
+ printf("Could not open %s for writing: %s\n", filename, strerror(errno));
}
- fclose(f);
- } else {
- printf("Could not open %s for writing: %s\n", filename, strerror(errno));
}
} else {
printf("Could not get screenshot!\n");
@@ -139,7 +229,7 @@ int main(int argc, char **argv)
screenshotr_client_free(shotr);
}
} else {
- printf("Could not start screenshotr service! Remember that you have to mount the Developer disk image on your device if you want to use the screenshotr service.\n");
+ printf("Could not start screenshotr service: %s\nRemember that you have to mount the Developer disk image on your device if you want to use the screenshotr service.\n", lockdownd_strerror(lerr));
}
if (service)
@@ -150,22 +240,3 @@ int main(int argc, char **argv)
return result;
}
-
-void print_usage(int argc, char **argv)
-{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] [FILE]\n", (name ? name + 1: argv[0]));
- printf("Gets a screenshot from a device.\n");
- printf("The screenshot is saved as a TIFF image with the given FILE name,\n");
- printf("where the default name is \"screenshot-DATE.tiff\", e.g.:\n");
- printf(" ./screenshot-2013-12-31-23-59-59.tiff\n\n");
- printf("NOTE: A mounted developer disk image is required on the device, otherwise\n");
- printf("the screenshotr service is not available.\n\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\n");
-}
diff --git a/tools/idevicesetlocation.c b/tools/idevicesetlocation.c
new file mode 100644
index 0000000..69fbaf5
--- /dev/null
+++ b/tools/idevicesetlocation.c
@@ -0,0 +1,192 @@
+/*
+ * idevicesetlocation.c
+ * Simulate location on iOS device with mounted developer disk image
+ *
+ * Copyright (c) 2016-2020 Nikias Bassen, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; 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 "idevicesetlocation"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <libimobiledevice/libimobiledevice.h>
+#include <libimobiledevice/lockdown.h>
+#include <libimobiledevice/service.h>
+
+#include <endianness.h>
+
+#define DT_SIMULATELOCATION_SERVICE "com.apple.dt.simulatelocation"
+
+enum {
+ SET_LOCATION = 0,
+ RESET_LOCATION = 1
+};
+
+static void print_usage(int argc, char **argv, int is_error)
+{
+ char *bname = strrchr(argv[0], '/');
+ bname = (bname) ? bname + 1 : argv[0];
+
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] -- <LAT> <LONG>\n", bname);
+ fprintf(is_error ? stderr : stdout, " %s [OPTIONS] reset\n", bname);
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "OPTIONS:\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 prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ int c = 0;
+ const struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+ { "debug", no_argument, NULL, 'd' },
+ { "network", no_argument, NULL, 'n' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
+ };
+ uint32_t mode = 0;
+ const char *udid = NULL;
+ int use_network = 0;
+
+ 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 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 ((argc > 2) || (argc < 1)) {
+ print_usage(argc+optind, argv-optind, 1);
+ return -1;
+ }
+
+ if (argc == 2) {
+ mode = SET_LOCATION;
+ } else if (argc == 1) {
+ if (strcmp(argv[0], "reset") == 0) {
+ mode = RESET_LOCATION;
+ } else {
+ print_usage(argc+optind, argv-optind, 1);
+ return -1;
+ }
+ }
+
+ idevice_t device = NULL;
+
+ if (idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS) {
+ if (udid) {
+ printf("ERROR: Device %s not found!\n", udid);
+ } else {
+ printf("ERROR: No device found!\n");
+ }
+ return -1;
+ }
+
+ lockdownd_client_t lockdown;
+ lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
+
+ lockdownd_service_descriptor_t svc = NULL;
+ lockdownd_error_t lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc);
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ 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;
+ }
+ lockdownd_client_free(lockdown);
+
+ service_client_t service = NULL;
+
+ service_error_t serr = service_client_new(device, svc, &service);
+
+ 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;
+ }
+
+ uint32_t l;
+ uint32_t s = 0;
+
+ l = htobe32(mode);
+ service_send(service, (const char*)&l, 4, &s);
+ if (mode == SET_LOCATION) {
+ int len = 4 + strlen(argv[0]) + 4 + strlen(argv[1]);
+ char *buf = malloc(len);
+ uint32_t latlen;
+ latlen = strlen(argv[0]);
+ l = htobe32(latlen);
+ memcpy(buf, &l, 4);
+ memcpy(buf+4, argv[0], latlen);
+ uint32_t longlen = strlen(argv[1]);
+ l = htobe32(longlen);
+ memcpy(buf+4+latlen, &l, 4);
+ memcpy(buf+4+latlen+4, argv[1], longlen);
+
+ s = 0;
+ service_send(service, buf, len, &s);
+ }
+
+ idevice_free(device);
+
+ return 0;
+}
diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c
index 5521736..a0e641d 100644
--- a/tools/idevicesyslog.c
+++ b/tools/idevicesyslog.c
@@ -2,7 +2,7 @@
* idevicesyslog.c
* Relay the syslog of a device to stdout
*
- * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2010-2020 Nikias Bassen, All Rights Reserved.
* Copyright (c) 2009 Martin Szulecki All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
@@ -24,6 +24,8 @@
#include "config.h"
#endif
+#define TOOL_NAME "idevicesyslog"
+
#include <stdio.h>
#include <string.h>
#include <errno.h>
@@ -39,34 +41,320 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/syslog_relay.h>
+#include <libimobiledevice-glue/termcolors.h>
static int quit_flag = 0;
+static int exit_on_disconnect = 0;
+static int show_device_name = 0;
static char* udid = NULL;
+static char** proc_filters = NULL;
+static int num_proc_filters = 0;
+static int proc_filter_excluding = 0;
+
+static int* pid_filters = NULL;
+static int num_pid_filters = 0;
+
+static char** msg_filters = NULL;
+static int num_msg_filters = 0;
+
+static char** trigger_filters = NULL;
+static int num_trigger_filters = 0;
+static char** untrigger_filters = NULL;
+static int num_untrigger_filters = 0;
+static int triggered = 0;
static idevice_t device = NULL;
static syslog_relay_client_t syslog = NULL;
-enum idevice_options lookup_opts = IDEVICE_LOOKUP_USBMUX | IDEVICE_LOOKUP_NETWORK;
+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 char *line = NULL;
+static int line_buffer_size = 0;
+static int lp = 0;
+
+static void add_filter(const char* filterstr)
+{
+ int filter_len = strlen(filterstr);
+ const char* start = filterstr;
+ const char* end = filterstr + filter_len;
+ const char* p = start;
+ while (p <= end) {
+ if ((*p == '|') || (*p == '\0')) {
+ if (p-start > 0) {
+ char* procn = malloc(p-start+1);
+ if (!procn) {
+ fprintf(stderr, "ERROR: malloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(procn, start, p-start);
+ procn[p-start] = '\0';
+ char* endp = NULL;
+ int pid_value = (int)strtol(procn, &endp, 10);
+ if (!endp || *endp == 0) {
+ int *new_pid_filters = realloc(pid_filters, sizeof(int) * (num_pid_filters+1));
+ if (!new_pid_filters) {
+ fprintf(stderr, "ERROR: realloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ pid_filters = new_pid_filters;
+ pid_filters[num_pid_filters] = pid_value;
+ num_pid_filters++;
+ } else {
+ char **new_proc_filters = realloc(proc_filters, sizeof(char*) * (num_proc_filters+1));
+ if (!new_proc_filters) {
+ fprintf(stderr, "ERROR: realloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ proc_filters = new_proc_filters;
+ proc_filters[num_proc_filters] = procn;
+ num_proc_filters++;
+ }
+ }
+ start = p+1;
+ }
+ p++;
+ }
+}
+
+static int find_char(char c, char** p, const char* end)
+{
+ while ((**p != c) && (*p < end)) {
+ (*p)++;
+ }
+ return (**p == c);
+}
+
+static void stop_logging(void);
static void syslog_callback(char c, void *user_data)
{
- putchar(c);
- if (c == '\n') {
- fflush(stdout);
+ if (lp >= line_buffer_size-1) {
+ line_buffer_size+=1024;
+ char* _line = realloc(line, line_buffer_size);
+ if (!_line) {
+ fprintf(stderr, "ERROR: realloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+ line = _line;
+ }
+ line[lp++] = c;
+ if (c == '\0') {
+ int shall_print = 0;
+ int trigger_off = 0;
+ lp--;
+ char* linep = &line[0];
+ do {
+ if (lp < 16) {
+ shall_print = 1;
+ cprintf(FG_WHITE);
+ break;
+ }
+
+ if (line[3] == ' ' && line[6] == ' ' && line[15] == ' ') {
+ char* end = &line[lp];
+ char* p = &line[16];
+
+ /* device name */
+ char* device_name_start = p;
+ char* device_name_end = p;
+ if (!find_char(' ', &p, end)) break;
+ device_name_end = p;
+ p++;
+
+ /* check if we have any triggers/untriggers */
+ if (num_untrigger_filters > 0 && triggered) {
+ int found = 0;
+ int i;
+ for (i = 0; i < num_untrigger_filters; i++) {
+ if (strstr(device_name_end+1, untrigger_filters[i])) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ shall_print = 1;
+ } else {
+ shall_print = 1;
+ trigger_off = 1;
+ }
+ } else if (num_trigger_filters > 0 && !triggered) {
+ int found = 0;
+ int i;
+ for (i = 0; i < num_trigger_filters; i++) {
+ if (strstr(device_name_end+1, trigger_filters[i])) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ shall_print = 0;
+ break;
+ }
+ triggered = 1;
+ shall_print = 1;
+ } else if (num_trigger_filters == 0 && num_untrigger_filters > 0 && !triggered) {
+ shall_print = 0;
+ quit_flag++;
+ break;
+ }
+
+ /* check message filters */
+ 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;
+ }
+
+ /* process name */
+ char* proc_name_start = p;
+ char* proc_name_end = p;
+ if (!find_char('[', &p, end)) break;
+ char* process_name_start = proc_name_start;
+ char* process_name_end = p;
+ char* pid_start = p+1;
+ char* pp = process_name_start;
+ if (find_char('(', &pp, p)) {
+ process_name_end = pp;
+ }
+ if (!find_char(']', &p, end)) break;
+ p++;
+ if (*p != ' ') break;
+ proc_name_end = p;
+ p++;
+
+ int proc_matched = 0;
+ if (num_pid_filters > 0) {
+ char* endp = NULL;
+ int pid_value = (int)strtol(pid_start, &endp, 10);
+ if (endp && (*endp == ']')) {
+ int found = proc_filter_excluding;
+ int i = 0;
+ for (i = 0; i < num_pid_filters; i++) {
+ if (pid_value == pid_filters[i]) {
+ found = !proc_filter_excluding;
+ break;
+ }
+ }
+ if (found) {
+ proc_matched = 1;
+ }
+ }
+ }
+ if (num_proc_filters > 0 && !proc_matched) {
+ int found = proc_filter_excluding;
+ int i = 0;
+ for (i = 0; i < num_proc_filters; i++) {
+ if (!proc_filters[i]) continue;
+ if (strncmp(proc_filters[i], process_name_start, process_name_end-process_name_start) == 0) {
+ found = !proc_filter_excluding;
+ break;
+ }
+ }
+ if (found) {
+ proc_matched = 1;
+ }
+ }
+ if (proc_matched) {
+ shall_print = 1;
+ } else {
+ if (num_pid_filters > 0 || num_proc_filters > 0) {
+ shall_print = 0;
+ break;
+ }
+ }
+
+ /* log level */
+ char* level_start = p;
+ char* level_end = p;
+ const char* level_color = NULL;
+ if (!strncmp(p, "<Notice>:", 9)) {
+ level_end += 9;
+ level_color = FG_GREEN;
+ } else if (!strncmp(p, "<Error>:", 8)) {
+ level_end += 8;
+ level_color = FG_RED;
+ } else if (!strncmp(p, "<Warning>:", 10)) {
+ level_end += 10;
+ level_color = FG_YELLOW;
+ } else if (!strncmp(p, "<Debug>:", 8)) {
+ level_end += 8;
+ level_color = FG_MAGENTA;
+ } else {
+ level_color = FG_WHITE;
+ }
+
+ /* write date and time */
+ cprintf(FG_LIGHT_GRAY);
+ fwrite(line, 1, 16, stdout);
+
+ if (show_device_name) {
+ /* write device name */
+ cprintf(FG_DARK_YELLOW);
+ fwrite(device_name_start, 1, device_name_end-device_name_start+1, stdout);
+ cprintf(COLOR_RESET);
+ }
+
+ /* write process name */
+ cprintf(FG_BRIGHT_CYAN);
+ fwrite(process_name_start, 1, process_name_end-process_name_start, stdout);
+ cprintf(FG_CYAN);
+ fwrite(process_name_end, 1, proc_name_end-process_name_end+1, stdout);
+
+ /* write log level */
+ cprintf(level_color);
+ if (level_end > level_start) {
+ fwrite(level_start, 1, level_end-level_start, stdout);
+ p = level_end;
+ }
+
+ lp -= p - linep;
+ linep = p;
+
+ cprintf(FG_WHITE);
+
+ } else {
+ shall_print = 1;
+ cprintf(FG_WHITE);
+ }
+ } while (0);
+
+ if ((num_msg_filters == 0 && num_proc_filters == 0 && num_pid_filters == 0 && num_trigger_filters == 0 && num_untrigger_filters == 0) || shall_print) {
+ fwrite(linep, 1, lp, stdout);
+ cprintf(COLOR_RESET);
+ fflush(stdout);
+ if (trigger_off) {
+ triggered = 0;
+ }
+ }
+ line[0] = '\0';
+ lp = 0;
+ return;
}
}
static int start_logging(void)
{
- idevice_error_t ret = idevice_new_with_options(&device, udid, lookup_opts);
+ idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
if (ret != IDEVICE_E_SUCCESS) {
fprintf(stderr, "Device with udid %s not found!?\n", udid);
return -1;
}
lockdownd_client_t lockdown = NULL;
- lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, "idevicesyslog");
+ lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
if (lerr != LOCKDOWN_E_SUCCESS) {
fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr);
idevice_free(device);
@@ -79,7 +367,7 @@ static int start_logging(void)
lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc);
if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
fprintf(stderr, "*** Device is passcode protected, enter passcode on the device to continue ***\n");
- while (1) {
+ while (!quit_flag) {
lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc);
if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) {
break;
@@ -107,7 +395,7 @@ static int start_logging(void)
}
/* start capturing syslog */
- serr = syslog_relay_start_capture(syslog, syslog_callback, NULL);
+ serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL);
if (serr != SYSLOG_RELAY_E_SUCCESS) {
fprintf(stderr, "ERROR: Unable tot start capturing syslog.\n");
syslog_relay_client_free(syslog);
@@ -117,7 +405,7 @@ static int start_logging(void)
return -1;
}
- fprintf(stdout, "[connected]\n");
+ fprintf(stdout, "[connected:%s]\n", udid);
fflush(stdout);
return 0;
@@ -140,6 +428,12 @@ static void stop_logging(void)
static void device_event_cb(const idevice_event_t* event, void* userdata)
{
+ if (use_network && event->conn_type != CONNECTION_NETWORK) {
+ return;
+ }
+ if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
+ return;
+ }
if (event->event == IDEVICE_DEVICE_ADD) {
if (!syslog) {
if (!udid) {
@@ -154,7 +448,10 @@ 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)) {
stop_logging();
- fprintf(stdout, "[disconnected]\n");
+ fprintf(stdout, "[disconnected:%s]\n", udid);
+ if (exit_on_disconnect) {
+ quit_flag++;
+ }
}
}
}
@@ -170,27 +467,71 @@ static void clean_exit(int sig)
static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
- name = strrchr(argv[0], '/');
+ char *name = strrchr(argv[0], '/');
fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
fprintf(is_error ? stderr : stdout,
- "Relay syslog of a connected device.\n\n" \
- " -u, --udid UDID target specific device by UDID\n" \
- " -n, --network connect to network device even if available via USB\n" \
- " -h, --help prints usage information\n" \
- " -d, --debug enable communication debugging\n" \
- "\n" \
- "Homepage: <" PACKAGE_URL ">\n"
+ "\n"
+ "Relay syslog of a connected device.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -n, --network connect to network device\n"
+ " -x, --exit exit when device disconnects\n"
+ " -h, --help prints usage information\n"
+ " -d, --debug enable communication debugging\n"
+ " -v, --version prints version information\n"
+ " --no-colors disable colored output\n"
+ " -o, --output FILE write to FILE instead of stdout\n"
+ " (existing FILE will be overwritten)\n"
+ " --colors force writing colored output, e.g. for --output\n"
+ "\n"
+ "FILTER OPTIONS:\n"
+ " -m, --match STRING only print messages that contain STRING\n"
+ " -t, --trigger STRING start logging when matching STRING\n"
+ " -T, --untrigger STRING stop logging when matching STRING\n"
+ " -p, --process PROCESS only print messages from matching process(es)\n"
+ " -e, --exclude PROCESS print all messages except matching process(es)\n"
+ " PROCESS is a process name or multiple process names\n"
+ " separated by \"|\".\n"
+ " -q, --quiet set a filter to exclude common noisy processes\n"
+ " --quiet-list prints the list of processes for --quiet and exits\n"
+ " -k, --kernel only print kernel messages\n"
+ " -K, --no-kernel suppress kernel messages\n"
+ "\n"
+ "For filter examples consult idevicesyslog(1) man page.\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
);
}
int main(int argc, char *argv[])
{
+ int include_filter = 0;
+ int exclude_filter = 0;
+ int include_kernel = 0;
+ int exclude_kernel = 0;
+ int force_colors = 0;
int c = 0;
const struct option longopts[] = {
{ "debug", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "udid", required_argument, NULL, 'u' },
+ { "network", no_argument, NULL, 'n' },
+ { "exit", no_argument, NULL, 'x' },
+ { "trigger", required_argument, NULL, 't' },
+ { "untrigger", required_argument, NULL, 'T' },
+ { "match", required_argument, NULL, 'm' },
+ { "process", required_argument, NULL, 'p' },
+ { "exclude", required_argument, NULL, 'e' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "kernel", no_argument, NULL, 'k' },
+ { "no-kernel", no_argument, NULL, 'K' },
+ { "quiet-list", no_argument, NULL, 1 },
+ { "no-colors", no_argument, NULL, 2 },
+ { "colors", no_argument, NULL, 3 },
+ { "output", required_argument, NULL, 'o' },
+ { "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0}
};
@@ -201,7 +542,7 @@ int main(int argc, char *argv[])
signal(SIGPIPE, SIG_IGN);
#endif
- while ((c = getopt_long(argc, argv, "dhu:n", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:e:p:qkKo:v", longopts, NULL)) != -1) {
switch (c) {
case 'd':
idevice_set_debug_level(1);
@@ -216,17 +557,165 @@ int main(int argc, char *argv[])
udid = strdup(optarg);
break;
case 'n':
- lookup_opts |= IDEVICE_LOOKUP_PREFER_NETWORK;
+ use_network = 1;
+ break;
+ case 'q':
+ exclude_filter++;
+ add_filter(QUIET_FILTER);
+ break;
+ case 'p':
+ case 'e':
+ if (c == 'p') {
+ include_filter++;
+ } else if (c == 'e') {
+ exclude_filter++;
+ }
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: filter string must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ add_filter(optarg);
+ break;
+ case 'm':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: message filter string must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ } else {
+ char **new_msg_filters = realloc(msg_filters, sizeof(char*) * (num_msg_filters+1));
+ if (!new_msg_filters) {
+ fprintf(stderr, "ERROR: realloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ msg_filters = new_msg_filters;
+ msg_filters[num_msg_filters] = strdup(optarg);
+ num_msg_filters++;
+ }
+ break;
+ case 't':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: trigger filter string must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ } else {
+ char **new_trigger_filters = realloc(trigger_filters, sizeof(char*) * (num_trigger_filters+1));
+ if (!new_trigger_filters) {
+ fprintf(stderr, "ERROR: realloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ trigger_filters = new_trigger_filters;
+ trigger_filters[num_trigger_filters] = strdup(optarg);
+ num_trigger_filters++;
+ }
+ break;
+ case 'T':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: untrigger filter string must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ } else {
+ char **new_untrigger_filters = realloc(untrigger_filters, sizeof(char*) * (num_untrigger_filters+1));
+ if (!new_untrigger_filters) {
+ fprintf(stderr, "ERROR: realloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ untrigger_filters = new_untrigger_filters;
+ untrigger_filters[num_untrigger_filters] = strdup(optarg);
+ num_untrigger_filters++;
+ }
+ break;
+ case 'k':
+ include_kernel++;
+ break;
+ case 'K':
+ exclude_kernel++;
+ break;
+ case 'x':
+ exit_on_disconnect = 1;
break;
case 'h':
print_usage(argc, argv, 0);
return 0;
+ case 1: {
+ printf("%s\n", QUIET_FILTER);
+ return 0;
+ }
+ case 2:
+ term_colors_set_enabled(0);
+ break;
+ case 3:
+ force_colors = 1;
+ break;
+ case 'o':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: --output option requires an argument!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ } else {
+ if (freopen(optarg, "w", stdout) == NULL) {
+ fprintf(stderr, "ERROR: Failed to open output file '%s' for writing: %s\n", optarg, strerror(errno));
+ return 1;
+ }
+ term_colors_set_enabled(0);
+ }
+ break;
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ return 0;
default:
print_usage(argc, argv, 1);
return 2;
}
}
+ if (force_colors) {
+ term_colors_set_enabled(1);
+ }
+
+ if (include_kernel > 0 && exclude_kernel > 0) {
+ fprintf(stderr, "ERROR: -k and -K cannot be used together.\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+
+ if (include_filter > 0 && exclude_filter > 0) {
+ fprintf(stderr, "ERROR: -p and -e/-q cannot be used together.\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ if (include_filter > 0 && exclude_kernel > 0) {
+ fprintf(stderr, "ERROR: -p and -K cannot be used together.\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+
+ if (exclude_filter > 0) {
+ proc_filter_excluding = 1;
+ if (include_kernel) {
+ int i = 0;
+ for (i = 0; i < num_proc_filters; i++) {
+ if (!strcmp(proc_filters[i], "kernel")) {
+ free(proc_filters[i]);
+ proc_filters[i] = NULL;
+ }
+ }
+ } else if (exclude_kernel) {
+ add_filter("kernel");
+ }
+ } else {
+ if (include_kernel) {
+ add_filter("kernel");
+ } else if (exclude_kernel) {
+ proc_filter_excluding = 1;
+ add_filter("kernel");
+ }
+ }
+
+ if (num_untrigger_filters > 0 && num_trigger_filters == 0) {
+ triggered = 1;
+ }
+
argc -= optind;
argv += optind;
@@ -238,19 +727,57 @@ int main(int argc, char *argv[])
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;
- } else {
- fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
}
+
+ fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
}
- idevice_event_subscribe(device_event_cb, NULL);
+ line_buffer_size = 1024;
+ line = malloc(line_buffer_size);
+
+ idevice_subscription_context_t context = NULL;
+ idevice_events_subscribe(&context, device_event_cb, NULL);
while (!quit_flag) {
sleep(1);
}
- idevice_event_unsubscribe();
+ idevice_events_unsubscribe(context);
stop_logging();
+ if (num_proc_filters > 0) {
+ int i;
+ for (i = 0; i < num_proc_filters; i++) {
+ free(proc_filters[i]);
+ }
+ free(proc_filters);
+ }
+ if (num_pid_filters > 0) {
+ free(pid_filters);
+ }
+ if (num_msg_filters > 0) {
+ int i;
+ for (i = 0; i < num_msg_filters; i++) {
+ free(msg_filters[i]);
+ }
+ free(msg_filters);
+ }
+ if (num_trigger_filters > 0) {
+ int i;
+ for (i = 0; i < num_trigger_filters; i++) {
+ free(trigger_filters[i]);
+ }
+ free(trigger_filters);
+ }
+ if (num_untrigger_filters > 0) {
+ int i;
+ for (i = 0; i < num_untrigger_filters; i++) {
+ free(untrigger_filters[i]);
+ }
+ free(untrigger_filters);
+ }
+
+ free(line);
+
free(udid);
return 0;