diff options
Diffstat (limited to 'adb/commandline.c')
-rw-r--r-- | adb/commandline.c | 268 |
1 files changed, 229 insertions, 39 deletions
diff --git a/adb/commandline.c b/adb/commandline.c index b0c2b80..75f337b 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -37,12 +37,6 @@ #include "adb_client.h" #include "file_sync_service.h" -enum { - IGNORE_DATA, - WIPE_DATA, - FLASH_DATA -}; - static int do_cmd(transport_type ttype, char* serial, char *cmd, ...); void get_my_path(char *s, size_t maxLen); @@ -135,14 +129,24 @@ void help() " adb bugreport - return all information from the device\n" " that should be included in a bug report.\n" "\n" + " adb backup [-f <file>] [-apk|-noapk] [-shared|-noshared] [-all] [<packages...>]\n" + " - Write an archive of the device's data to <file>.\n" + " If a -f option is not supplied then the data is\n" + " written to \"backup.ab\" in the current directory.\n" + " (-apk|-noapk enable/disable backup of the .apks themselves\n" + " in the archive; the default is noapk.)\n" + " (-shared|-noshared enable/disable backup of the device's\n" + " shared storage / SD card contents; the default is noshared.)\n" + " (-all means to back up all installed applications)\n" + " (<packages...> is the list of applications to be backed up. If\n" + " the -all or -shared flags are passed, then the package\n" + " list is optional.)\n" + "\n" + " adb restore <file> - restore device contents from the <file> backup archive\n" + "\n" " adb help - show this help message\n" " adb version - show version num\n" "\n" - "DATAOPTS:\n" - " (no option) - don't touch the data partition\n" - " -w - wipe the data partition\n" - " -d - flash the data partition\n" - "\n" "scripting:\n" " adb wait-for-device - block until device is online\n" " adb start-server - ensure that there is a server running\n" @@ -218,7 +222,9 @@ static void read_and_dump(int fd) int len; while(fd >= 0) { + D("read_and_dump(): pre adb_read(fd=%d)\n", fd); len = adb_read(fd, buf, 4096); + D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len); if(len == 0) { break; } @@ -232,6 +238,34 @@ static void read_and_dump(int fd) } } +static void copy_to_file(int inFd, int outFd) { + const size_t BUFSIZE = 32 * 1024; + char* buf = (char*) malloc(BUFSIZE); + int len; + long total = 0; + + D("copy_to_file(%d -> %d)\n", inFd, outFd); + for (;;) { + len = adb_read(inFd, buf, BUFSIZE); + if (len == 0) { + D("copy_to_file() : read 0 bytes; exiting\n"); + break; + } + if (len < 0) { + if (errno == EINTR) { + D("copy_to_file() : EINTR, retrying\n"); + continue; + } + D("copy_to_file() : error %d\n", errno); + break; + } + adb_write(outFd, buf, len); + total += len; + } + D("copy_to_file() finished after %lu bytes\n", total); + free(buf); +} + static void *stdin_read_thread(void *x) { int fd, fdi; @@ -246,7 +280,9 @@ static void *stdin_read_thread(void *x) for(;;) { /* fdi is really the client's stdin, so use read, not adb_read here */ + D("stdin_read_thread(): pre unix_read(fdi=%d,...)\n", fdi); r = unix_read(fdi, buf, 1024); + D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", fdi); if(r == 0) break; if(r < 0) { if(errno == EINTR) continue; @@ -537,6 +573,85 @@ static int logcat(transport_type transport, char* serial, int argc, char **argv) return 0; } +static int backup(int argc, char** argv) { + char buf[4096]; + const char* filename = "./backup.ab"; + int fd, outFd; + int i, j; + + /* find, extract, and use any -f argument */ + for (i = 1; i < argc; i++) { + if (!strcmp("-f", argv[i])) { + if (i == argc-1) { + fprintf(stderr, "adb: -f passed with no filename\n"); + return usage(); + } + filename = argv[i+1]; + for (j = i+2; j <= argc; ) { + argv[i++] = argv[j++]; + } + argc -= 2; + argv[argc] = NULL; + } + } + + /* bare "adb backup" or "adb backup -f filename" are not valid invocations */ + if (argc < 2) return usage(); + + outFd = adb_open_mode(filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); + if (outFd < 0) { + fprintf(stderr, "adb: unable to open file %s\n", filename); + return -1; + } + + snprintf(buf, sizeof(buf), "backup"); + for (argc--, argv++; argc; argc--, argv++) { + strncat(buf, ":", sizeof(buf) - strlen(buf) - 1); + strncat(buf, argv[0], sizeof(buf) - strlen(buf) - 1); + } + + D("backup. filename=%s buf=%s\n", filename, buf); + fd = adb_connect(buf); + if (fd < 0) { + fprintf(stderr, "adb: unable to connect for backup\n"); + adb_close(outFd); + return -1; + } + + copy_to_file(fd, outFd); + + adb_close(fd); + adb_close(outFd); + return 0; +} + +static int restore(int argc, char** argv) { + const char* filename; + int fd, tarFd; + + if (argc != 2) return usage(); + + filename = argv[1]; + tarFd = adb_open(filename, O_RDONLY); + if (tarFd < 0) { + fprintf(stderr, "adb: unable to open file %s\n", filename); + return -1; + } + + fd = adb_connect("restore:"); + if (fd < 0) { + fprintf(stderr, "adb: unable to connect for backup\n"); + adb_close(tarFd); + return -1; + } + + copy_to_file(tarFd, fd); + + adb_close(fd); + adb_close(tarFd); + return 0; +} + #define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make" static int top_works(const char *top) { @@ -853,6 +968,7 @@ top: } if(argc < 2) { + D("starting interactive shell\n"); r = interactive_shell(); if (h) { printf("\x1b[0m"); @@ -877,9 +993,12 @@ top: } for(;;) { + D("interactive shell loop. buff=%s\n", buf); fd = adb_connect(buf); if(fd >= 0) { + D("about to read_and_dump(fd=%d)\n", fd); read_and_dump(fd); + D("read_and_dump() done.\n"); adb_close(fd); r = 0; } else { @@ -896,6 +1015,7 @@ top: printf("\x1b[0m"); fflush(stdout); } + D("interactive shell loop. return r=%d\n", r); return r; } } @@ -1091,6 +1211,14 @@ top: return adb_connect("host:start-server"); } + if (!strcmp(argv[0], "backup")) { + return backup(argc, argv); + } + + if (!strcmp(argv[0], "restore")) { + return restore(argc, argv); + } + if (!strcmp(argv[0], "jdwp")) { int fd = adb_connect("jdwp"); if (fd >= 0) { @@ -1242,50 +1370,112 @@ static int delete_file(transport_type transport, char* serial, char* filename) return 0; } -int install_app(transport_type transport, char* serial, int argc, char** argv) +static const char* get_basename(const char* filename) +{ + const char* basename = adb_dirstop(filename); + if (basename) { + basename++; + return basename; + } else { + return filename; + } +} + +static int check_file(const char* filename) { struct stat st; - int err; - const char *const DATA_DEST = "/data/local/tmp/%s"; - const char *const SD_DEST = "/sdcard/tmp/%s"; + + if (filename == NULL) { + return 0; + } + + if (stat(filename, &st) != 0) { + fprintf(stderr, "can't find '%s' to install\n", filename); + return 1; + } + + if (!S_ISREG(st.st_mode)) { + fprintf(stderr, "can't install '%s' because it's not a file\n", filename); + return 1; + } + + return 0; +} + +int install_app(transport_type transport, char* serial, int argc, char** argv) +{ + static const char *const DATA_DEST = "/data/local/tmp/%s"; + static const char *const SD_DEST = "/sdcard/tmp/%s"; const char* where = DATA_DEST; - char to[PATH_MAX]; - char* filename = argv[argc - 1]; - const char* p; + char apk_dest[PATH_MAX]; + char verification_dest[PATH_MAX]; + char* apk_file; + char* verification_file = NULL; + int file_arg = -1; + int err; int i; - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "-s")) + for (i = 1; i < argc; i++) { + if (*argv[i] != '-') { + file_arg = i; + break; + } else if (!strcmp(argv[i], "-s")) { where = SD_DEST; + } } - p = adb_dirstop(filename); - if (p) { - p++; - snprintf(to, sizeof to, where, p); - } else { - snprintf(to, sizeof to, where, filename); + if (file_arg < 0) { + fprintf(stderr, "can't find filename in arguments"); + return 1; + } else if (file_arg + 2 < argc) { + fprintf(stderr, "too many files specified; only takes APK file and verifier file"); + return 1; } - if (p[0] == '\0') { + + apk_file = argv[file_arg]; + + if (file_arg != argc - 1) { + verification_file = argv[file_arg + 1]; } - err = stat(filename, &st); - if (err != 0) { - fprintf(stderr, "can't find '%s' to install\n", filename); + if (check_file(apk_file) || check_file(verification_file)) { return 1; } - if (!S_ISREG(st.st_mode)) { - fprintf(stderr, "can't install '%s' because it's not a file\n", - filename); - return 1; + + snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file)); + if (verification_file != NULL) { + snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file)); + + if (!strcmp(apk_dest, verification_dest)) { + fprintf(stderr, "APK and verification file can't have the same name\n"); + return 1; + } + } + + err = do_sync_push(apk_file, apk_dest, 1 /* verify APK */); + if (err) { + return err; + } else { + argv[file_arg] = apk_dest; /* destination name, not source location */ } - if (!(err = do_sync_push(filename, to, 1 /* verify APK */))) { - /* file in place; tell the Package Manager to install it */ - argv[argc - 1] = to; /* destination name, not source location */ - pm_command(transport, serial, argc, argv); - delete_file(transport, serial, to); + if (verification_file != NULL) { + err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */); + if (err) { + goto cleanup_apk; + } else { + argv[file_arg + 1] = verification_dest; /* destination name, not source location */ + } + } + + pm_command(transport, serial, argc, argv); + + if (verification_file != NULL) { + delete_file(transport, serial, verification_dest); } +cleanup_apk: + delete_file(transport, serial, apk_dest); + return err; } |