From d2f5415c603f7d9961f7a0b05579a0768e071410 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Thu, 21 Apr 2011 12:53:28 -0700 Subject: [PATCH] Add 'adb backup' for pulling a full backup tarfile to the host The direct command interfaces with the 'bu' binary in /system/bin on the device. Change-Id: I4cd69eedfe5144c47277573c5626c6ad8755d70b --- adb/Android.mk | 2 + adb/adb.h | 3 +- adb/backup_service.c | 95 ++++++++++++++++++++++++++++++++++++++++++++ adb/commandline.c | 75 ++++++++++++++++++++++++++++++++++ adb/services.c | 4 ++ 5 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 adb/backup_service.c diff --git a/adb/Android.mk b/adb/Android.mk index 6ed31eb1f..e893ca91e 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -113,6 +113,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ adb.c \ + backup_service.c \ fdevent.c \ transport.c \ transport_local.c \ @@ -164,6 +165,7 @@ LOCAL_LDLIBS := -lrt -lncurses -lpthread LOCAL_SRC_FILES := \ adb.c \ + backup_service.c \ console.c \ transport.c \ transport_local.c \ diff --git a/adb/adb.h b/adb/adb.h index 2908f1e5a..318a2d8b2 100644 --- a/adb/adb.h +++ b/adb/adb.h @@ -35,7 +35,7 @@ #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 26 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 27 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; @@ -304,6 +304,7 @@ int create_jdwp_connection_fd(int jdwp_pid); #endif #if !ADB_HOST +int backup_service(char* args); void framebuffer_service(int fd, void *cookie); void log_service(int fd, void *cookie); void remount_service(int fd, void *cookie); diff --git a/adb/backup_service.c b/adb/backup_service.c new file mode 100644 index 000000000..f5dc0b297 --- /dev/null +++ b/adb/backup_service.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb.h" + +/* returns the data socket passing the backup data here for forwarding */ +int backup_service(char* args) { + pid_t pid; + int s[2]; + + D("backup_service(%s)\n", args); + + // set up the pipe from the subprocess to here + // parent will read s[0]; child will write s[1] + if (adb_socketpair(s)) { + D("can't create backup socketpair\n"); + fprintf(stderr, "unable to create backup socketpair\n"); + return -1; + } + + // spin off the child process to run the backup command + pid = fork(); + if (pid < 0) { + // failure + D("can't fork for backup\n"); + fprintf(stderr, "unable to fork for backup\n"); + adb_close(s[0]); + adb_close(s[1]); + return -1; + } + + // Great, we're off and running. + if (pid == 0) { + char* p; + int argc; + char** backup_args; + + // child -- actually run the backup here + argc = 1; // room for the basic 'bu' argv[0] + for (p = (char*)args; p && *p; ) { + argc++; + while (*p && *p != ':') p++; + if (*p == ':') p++; + } + + backup_args = (char**) alloca(argc*sizeof(char*) + 1); + backup_args[0] = "bu"; + argc = 1; // run through again to build the argv array + for (p = (char*)args; *p; ) { + backup_args[argc++] = p; + while (*p && *p != ':') p++; + if (*p == ':') { + *p = 0; + p++; + } + } + backup_args[argc] = NULL; + + // Close the half of the socket that we don't care about, route 'bu's console + // to the output socket, and off we go + adb_close(s[0]); + dup2(s[1], STDOUT_FILENO); + + // off we go + execvp("/system/bin/bu", (char * const *)backup_args); + // oops error - close up shop and go home + fprintf(stderr, "Unable to exec 'bu', bailing\n"); + exit(-1); + } else { + // parent, i.e. adbd -- close the sending half of the socket + adb_close(s[1]); + } + + // we'll be reading from s[0] as the data is sent by the child process + return s[0]; +} diff --git a/adb/commandline.c b/adb/commandline.c index bd71bfeff..f8cdbf490 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -129,6 +129,19 @@ void help() " adb bugreport - return all information from the device\n" " that should be included in a bug report.\n" "\n" + " adb backup [-f ] [-apk|-noapk] [-shared|-noshared] [-all] []\n" + " - Write a tarfile backup of the device's data to .\n" + " The -f option must come first; if not specified then the data\n" + " is written to \"backup.tar\" in the current directory.\n" + " (-apk|-noapk enable/disable backup of the .apks themselves\n" + " in the tarfile; 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" + " ( 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 help - show this help message\n" " adb version - show version num\n" "\n" @@ -223,6 +236,25 @@ static void read_and_dump(int fd) } } +static void copy_to_file(int inFd, int outFd) { + char buf[4096]; + int len; + + D("copy_to_file(%d -> %d)\n", inFd, outFd); + for (;;) { + len = adb_read(inFd, buf, sizeof(buf)); + if (len == 0) { + break; + } + if (len < 0) { + if (errno == EINTR) continue; + D("copy_to_file() : error %d\n", errno); + break; + } + adb_write(outFd, buf, len); + } +} + static void *stdin_read_thread(void *x) { int fd, fdi; @@ -530,6 +562,45 @@ 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.tar"; + int fd, outFd; + + if (!strcmp("-f", argv[1])) { + if (argc < 3) return usage(); + filename = argv[2]; + argc -= 2; + argv += 2; + } + + 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; +} + #define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make" static int top_works(const char *top) { @@ -1089,6 +1160,10 @@ top: return adb_connect("host:start-server"); } + if (!strcmp(argv[0], "backup")) { + return backup(argc, argv); + } + if (!strcmp(argv[0], "jdwp")) { int fd = adb_connect("jdwp"); if (fd >= 0) { diff --git a/adb/services.c b/adb/services.c index 43f9174f2..ec0b0bad0 100644 --- a/adb/services.c +++ b/adb/services.c @@ -479,6 +479,10 @@ int service_to_fd(const char *name) ret = create_service_thread(reboot_service, arg); } else if(!strncmp(name, "root:", 5)) { ret = create_service_thread(restart_root_service, NULL); + } else if(!strncmp(name, "backup:", 7)) { + char* arg = strdup(name+7); + if (arg == NULL) return -1; + ret = backup_service(arg); } else if(!strncmp(name, "tcpip:", 6)) { int port; if (sscanf(name + 6, "%d", &port) == 0) {