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) {