summaryrefslogtreecommitdiffstats
path: root/tools/idevicebackup2.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/idevicebackup2.c')
-rw-r--r--tools/idevicebackup2.c661
1 files changed, 411 insertions, 250 deletions
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
index 58fda8d..c73b269 100644
--- a/tools/idevicebackup2.c
+++ b/tools/idevicebackup2.c
@@ -2,8 +2,8 @@
* idevicebackup2.c
* Command line interface to use the device's backup and restore service
*
- * Copyright (c) 2010-2018 Nikias Bassen All Rights Reserved.
- * Copyright (c) 2009-2010 Martin Szulecki 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
* modify it under the terms of the GNU Lesser General Public
@@ -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>
@@ -42,7 +45,9 @@
#include <libimobiledevice/afc.h>
#include <libimobiledevice/installation_proxy.h>
#include <libimobiledevice/sbservices.h>
-#include "common/utils.h"
+#include <libimobiledevice/diagnostics_relay.h>
+#include <libimobiledevice-glue/utils.h>
+#include <plist/plist.h>
#include <endianness.h>
@@ -53,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>
@@ -177,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);
@@ -345,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;
}
@@ -366,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);
@@ -375,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");
}
@@ -545,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;
@@ -598,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");
@@ -626,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) {
@@ -737,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;
}
}
@@ -954,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;
@@ -1349,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);
@@ -1397,49 +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 system 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))
+
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;
@@ -1453,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);
@@ -1464,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);
}
@@ -1680,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");
@@ -1700,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);
@@ -1721,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);
}
@@ -1739,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;
@@ -1757,8 +1843,26 @@ int main(int argc, char *argv[])
node_tmp = NULL;
}
+ /* get ProductVersion */
+ char *product_version = NULL;
+ int device_version = 0;
+ node_tmp = NULL;
+ lockdownd_get_value(lockdown, NULL, "ProductVersion", &node_tmp);
+ if (node_tmp) {
+ if (plist_get_node_type(node_tmp) == PLIST_STRING) {
+ plist_get_string_val(node_tmp, &product_version);
+ }
+ plist_free(node_tmp);
+ node_tmp = NULL;
+ }
+ if (product_version) {
+ int vers[3] = { 0, 0, 0 };
+ if (sscanf(product_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
+ device_version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
+ }
+ }
+
/* 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);
@@ -1772,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;
}
}
@@ -1792,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;
@@ -1827,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");
@@ -1842,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);
@@ -1855,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");
@@ -1921,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);
@@ -1982,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));
}
@@ -1998,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 */
@@ -2055,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) {
@@ -2071,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 {
@@ -2108,6 +2228,32 @@ checkpoint:
}
if (newpw || backup_password) {
mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
+ 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, 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"));
+ if (diagnostics_relay_query_mobilegestalt(diag, keys, &dict) == DIAGNOSTICS_RELAY_E_SUCCESS) {
+ plist_t node = plist_access_path(dict, 2, "MobileGestalt", "PasswordConfigured");
+ plist_get_bool_val(node, &passcode_hint);
+ }
+ plist_free(keys);
+ plist_free(dict);
+ diagnostics_relay_goodbye(diag);
+ diagnostics_relay_client_free(diag);
+ }
+ }
+ if (passcode_hint) {
+ if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
+ PRINT_VERBOSE(1, "Please confirm changing the backup password by entering the passcode on the device.\n");
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
+ PRINT_VERBOSE(1, "Please confirm enabling the backup encryption by entering the passcode on the device.\n");
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
+ PRINT_VERBOSE(1, "Please confirm disabling the backup encryption by entering the passcode on the device.\n");
+ }
+ }
/*if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
int retr = 10;
while ((retr-- >= 0) && !backup_domain_changed) {
@@ -2128,6 +2274,7 @@ checkpoint:
int operation_ok = 0;
plist_t message = NULL;
+ mobilebackup2_error_t mberr;
char *dlmsg = NULL;
int file_count = 0;
int errcode = 0;
@@ -2138,10 +2285,13 @@ checkpoint:
do {
free(dlmsg);
dlmsg = NULL;
- mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
- if (!message || !dlmsg) {
- PRINT_VERBOSE(1, "Device is not ready yet. Going to try again in 2 seconds...\n");
- sleep(2);
+ mberr = mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
+ if (mberr == MOBILEBACKUP2_E_RECEIVE_TIMEOUT) {
+ PRINT_VERBOSE(2, "Device is not ready yet, retrying...\n");
+ goto files_out;
+ } else if (mberr != MOBILEBACKUP2_E_SUCCESS) {
+ PRINT_VERBOSE(0, "ERROR: Could not receive from mobilebackup2 (%d)\n", mberr);
+ quit_flag++;
goto files_out;
}
@@ -2172,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);
@@ -2359,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);
@@ -2447,12 +2602,18 @@ files_out:
}
break;
case CMD_RESTORE:
- if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0)
- PRINT_VERBOSE(1, "The device should reboot now.\n");
if (operation_ok) {
+ if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0)
+ PRINT_VERBOSE(1, "The device should reboot now.\n");
PRINT_VERBOSE(1, "Restore Successful.\n");
} else {
- PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code);
+ afc_remove_path(afc, "/iTunesRestore/RestoreApplications.plist");
+ afc_remove_path(afc, "/iTunesRestore");
+ if (quit_flag) {
+ PRINT_VERBOSE(1, "Restore Aborted.\n");
+ } else {
+ PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code);
+ }
}
break;
case CMD_INFO:
@@ -2477,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;
}