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
diff --git a/adb/Android.mk b/adb/Android.mk
index 6ed31eb..e893ca9 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -113,6 +113,7 @@
LOCAL_SRC_FILES := \
adb.c \
+ backup_service.c \
fdevent.c \
transport.c \
transport_local.c \
@@ -164,6 +165,7 @@
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 2908f1e..318a2d8 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 @@
#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 0000000..f5dc0b2
--- /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 <unistd.h>
+#include <stdio.h>
+
+#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 bd71bfe..f8cdbf4 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -129,6 +129,19 @@
" 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 a tarfile backup of the device's data to <file>.\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"
+ " (<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 help - show this help message\n"
" adb version - show version num\n"
"\n"
@@ -223,6 +236,25 @@
}
}
+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 @@
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 @@
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 43f9174..ec0b0ba 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -479,6 +479,10 @@
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) {