Add "exec" service: shell commands with no pty.

To facilitate device scripts that want to read/write binary data from
the host side, this change introduces a new "exec" service that
behaves like "shell" but without creating a pty, which would otherwise
mangle binary data.

After forking, it hooks up stdin/stdout of the child process to
the socket connected through to the host.  The adb transport doesn't
support shutdown(), so the host can't half-close the socket and wait
for device termination.  Instead, the host side now has two explicit
commands "exec-in" and "exec-out" for either sending or receiving
data.

Teach host side copy_to_file() to deal with stdin/stdout special
cases.  Switch device side backup/restore services to use the new
create_subproc_raw under the hood.

Change-Id: I5993049803519d3959761f2363037b02c50920ee
diff --git a/adb/Android.mk b/adb/Android.mk
index 50e28a6..80427b8 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -102,7 +102,6 @@
 
 LOCAL_SRC_FILES := \
 	adb.c \
-	backup_service.c \
 	fdevent.c \
 	transport.c \
 	transport_local.c \
diff --git a/adb/adb.h b/adb/adb.h
index 6f5c93c..2674897 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -324,11 +324,6 @@
 #endif
 
 #if !ADB_HOST
-typedef enum {
-    BACKUP,
-    RESTORE
-} BackupOperation;
-int backup_service(BackupOperation operation, char* args);
 void framebuffer_service(int fd, void *cookie);
 void remount_service(int fd, void *cookie);
 #endif
@@ -416,7 +411,7 @@
 #  define  D(...)          ((void)0)
 #  define  DR(...)         ((void)0)
 #  define  ADB_TRACING     0
-#endif
+#endif /* ADB_TRACE */
 
 
 #if !DEBUG_PACKETS
@@ -474,6 +469,11 @@
 extern int HOST;
 extern int SHELL_EXIT_NOTIFY_FD;
 
+typedef enum {
+    SUBPROC_PTY = 0,
+    SUBPROC_RAW = 1,
+} subproc_mode;
+
 #define CHUNK_SIZE (64*1024)
 
 #if !ADB_HOST
diff --git a/adb/backup_service.c b/adb/backup_service.c
deleted file mode 100644
index 654e0f3..0000000
--- a/adb/backup_service.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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"
-
-typedef struct {
-    pid_t pid;
-    int fd;
-} backup_harvest_params;
-
-// socketpair but do *not* mark as close_on_exec
-static int backup_socketpair(int sv[2]) {
-    int rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
-    if (rc < 0)
-        return -1;
-
-    return 0;
-}
-
-// harvest the child process then close the read end of the socketpair
-static void* backup_child_waiter(void* args) {
-    int status;
-    backup_harvest_params* params = (backup_harvest_params*) args;
-
-    waitpid(params->pid, &status, 0);
-    adb_close(params->fd);
-    free(params);
-    return NULL;
-}
-
-/* returns the data socket passing the backup data here for forwarding */
-int backup_service(BackupOperation op, char* args) {
-    pid_t pid;
-    int s[2];
-    char* operation;
-
-    // Command string depends on our invocation
-    if (op == BACKUP) {
-        operation = "backup";
-    } else {
-        operation = "restore";
-    }
-
-    D("backup_service(%s, %s)\n", operation, args);
-
-    // set up the pipe from the subprocess to here
-    // parent will read s[0]; child will write s[1]
-    if (backup_socketpair(s)) {
-        D("can't create backup/restore socketpair\n");
-        fprintf(stderr, "unable to create backup/restore socketpair\n");
-        return -1;
-    }
-
-    D("Backup/restore socket pair: (send=%d, receive=%d)\n", s[1], s[0]);
-    close_on_exec(s[0]);    // only the side we hold on to
-
-    // spin off the child process to run the backup command
-    pid = fork();
-    if (pid < 0) {
-        // failure
-        D("can't fork for %s\n", operation);
-        fprintf(stderr, "unable to fork for %s\n", operation);
-        adb_close(s[0]);
-        adb_close(s[1]);
-        return -1;
-    }
-
-    // Great, we're off and running.
-    if (pid == 0) {
-        // child -- actually run the backup here
-        char* p;
-        int argc;
-        char portnum[16];
-        char** bu_args;
-
-        // fixed args:  [0] is 'bu', [1] is the port number, [2] is the 'operation' string
-        argc = 3;
-        for (p = (char*)args; p && *p; ) {
-            argc++;
-            while (*p && *p != ':') p++;
-            if (*p == ':') p++;
-        }
-
-        bu_args = (char**) alloca(argc*sizeof(char*) + 1);
-
-        // run through again to build the argv array
-        argc = 0;
-        bu_args[argc++] = "bu";
-        snprintf(portnum, sizeof(portnum), "%d", s[1]);
-        bu_args[argc++] = portnum;
-        bu_args[argc++] = operation;
-        for (p = (char*)args; p && *p; ) {
-            bu_args[argc++] = p;
-            while (*p && *p != ':') p++;
-            if (*p == ':') {
-                *p = 0;
-                p++;
-            }
-        }
-        bu_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]);
-
-        // off we go
-        execvp("/system/bin/bu", (char * const *)bu_args);
-        // oops error - close up shop and go home
-        fprintf(stderr, "Unable to exec 'bu', bailing\n");
-        exit(-1);
-    } else {
-        adb_thread_t t;
-        backup_harvest_params* params;
-
-        // parent, i.e. adbd -- close the sending half of the socket
-        D("fork() returned pid %d\n", pid);
-        adb_close(s[1]);
-
-        // spin a thread to harvest the child process
-        params = (backup_harvest_params*) malloc(sizeof(backup_harvest_params));
-        params->pid = pid;
-        params->fd = s[0];
-        if (adb_thread_create(&t, backup_child_waiter, params)) {
-            adb_close(s[0]);
-            free(params);
-            D("Unable to create child harvester\n");
-            return -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 a3eaf43..2cadc72 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -274,8 +274,17 @@
     long total = 0;
 
     D("copy_to_file(%d -> %d)\n", inFd, outFd);
+#ifdef HAVE_TERMIO_H
+    if (inFd == STDIN_FILENO) {
+        stdin_raw_init(STDIN_FILENO);
+    }
+#endif
     for (;;) {
-        len = adb_read(inFd, buf, BUFSIZE);
+        if (inFd == STDIN_FILENO) {
+            len = unix_read(inFd, buf, BUFSIZE);
+        } else {
+            len = adb_read(inFd, buf, BUFSIZE);
+        }
         if (len == 0) {
             D("copy_to_file() : read 0 bytes; exiting\n");
             break;
@@ -288,9 +297,19 @@
             D("copy_to_file() : error %d\n", errno);
             break;
         }
-        adb_write(outFd, buf, len);
+        if (outFd == STDOUT_FILENO) {
+            fwrite(buf, 1, len, stdout);
+            fflush(stdout);
+        } else {
+            adb_write(outFd, buf, len);
+        }
         total += len;
     }
+#ifdef HAVE_TERMIO_H
+    if (inFd == STDIN_FILENO) {
+        stdin_raw_restore(STDIN_FILENO);
+    }
+#endif
     D("copy_to_file() finished after %lu bytes\n", total);
     free(buf);
 }
@@ -927,7 +946,6 @@
     return path_buf;
 }
 
-
 static void parse_push_pull_args(char **arg, int narg, char const **path1, char const **path2,
                                  int *show_progress, int *copy_attrs) {
     *show_progress = 0;
@@ -964,7 +982,6 @@
     int is_server = 0;
     int persist = 0;
     int r;
-    int quote;
     transport_type ttype = kTransportAny;
     char* serial = NULL;
     char* server_port_str = NULL;
@@ -1185,19 +1202,14 @@
             return r;
         }
 
-        snprintf(buf, sizeof buf, "shell:%s", argv[1]);
+        snprintf(buf, sizeof(buf), "shell:%s", argv[1]);
         argc -= 2;
         argv += 2;
-        while(argc-- > 0) {
-            strcat(buf, " ");
-
-            /* quote empty strings and strings with spaces */
-            quote = (**argv == 0 || strchr(*argv, ' '));
-            if (quote)
-                strcat(buf, "\"");
-            strcat(buf, *argv++);
-            if (quote)
-                strcat(buf, "\"");
+        while (argc-- > 0) {
+            char *quoted = dupAndQuote(*argv++);
+            strncat(buf, " ", sizeof(buf) - 1);
+            strncat(buf, quoted, sizeof(buf) - 1);
+            free(quoted);
         }
 
         for(;;) {
@@ -1229,6 +1241,36 @@
         }
     }
 
+    if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
+        int exec_in = !strcmp(argv[0], "exec-in");
+        int fd;
+
+        snprintf(buf, sizeof buf, "exec:%s", argv[1]);
+        argc -= 2;
+        argv += 2;
+        while (argc-- > 0) {
+            char *quoted = dupAndQuote(*argv++);
+            strncat(buf, " ", sizeof(buf) - 1);
+            strncat(buf, quoted, sizeof(buf) - 1);
+            free(quoted);
+        }
+
+        fd = adb_connect(buf);
+        if (fd < 0) {
+            fprintf(stderr, "error: %s\n", adb_error());
+            return -1;
+        }
+
+        if (exec_in) {
+            copy_to_file(STDIN_FILENO, fd);
+        } else {
+            copy_to_file(fd, STDOUT_FILENO);
+        }
+
+        adb_close(fd);
+        return 0;
+    }
+
     if(!strcmp(argv[0], "kill-server")) {
         int fd;
         fd = _adb_connect("host:kill");
diff --git a/adb/services.c b/adb/services.c
index e25a232..50b81ab 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -173,11 +173,26 @@
 }
 
 #if !ADB_HOST
-static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
+
+static void init_subproc_child()
 {
+    setsid();
+
+    // Set OOM adjustment to prevent killing
+    int fd = adb_open("/proc/self/oom_adj", O_WRONLY);
+    if (fd >= 0) {
+        adb_write(fd, "0", 1);
+        adb_close(fd);
+    } else {
+       D("adb: unable to update oom_adj\n");
+    }
+}
+
+static int create_subproc_pty(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
+{
+    D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
 #ifdef HAVE_WIN32_PROC
-    D("create_subprocess(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
-    fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
+    fprintf(stderr, "error: create_subproc_pty not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
     return -1;
 #else /* !HAVE_WIN32_PROC */
     char *devname;
@@ -204,47 +219,74 @@
         return -1;
     }
 
-    if(*pid == 0){
-        int pts;
+    if (*pid == 0) {
+        init_subproc_child();
 
-        setsid();
-
-        pts = unix_open(devname, O_RDWR);
-        if(pts < 0) {
+        int pts = unix_open(devname, O_RDWR);
+        if (pts < 0) {
             fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname);
             exit(-1);
         }
 
-        dup2(pts, 0);
-        dup2(pts, 1);
-        dup2(pts, 2);
+        dup2(pts, STDIN_FILENO);
+        dup2(pts, STDOUT_FILENO);
+        dup2(pts, STDERR_FILENO);
 
         adb_close(pts);
         adb_close(ptm);
 
-        // set OOM adjustment to zero
-        char text[64];
-        snprintf(text, sizeof text, "/proc/%d/oom_adj", getpid());
-        int fd = adb_open(text, O_WRONLY);
-        if (fd >= 0) {
-            adb_write(fd, "0", 1);
-            adb_close(fd);
-        } else {
-           D("adb: unable to open %s\n", text);
-        }
         execl(cmd, cmd, arg0, arg1, NULL);
         fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
                 cmd, strerror(errno), errno);
         exit(-1);
     } else {
-        // Don't set child's OOM adjustment to zero.
-        // Let the child do it itself, as sometimes the parent starts
-        // running before the child has a /proc/pid/oom_adj.
-        // """adb: unable to open /proc/644/oom_adj""" seen in some logs.
         return ptm;
     }
 #endif /* !HAVE_WIN32_PROC */
 }
+
+static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
+{
+    D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
+#ifdef HAVE_WIN32_PROC
+    fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
+    return -1;
+#else /* !HAVE_WIN32_PROC */
+
+    // 0 is parent socket, 1 is child socket
+    int sv[2];
+    if (unix_socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
+        printf("[ cannot create socket pair - %s ]\n", strerror(errno));
+        return -1;
+    }
+
+    *pid = fork();
+    if (*pid < 0) {
+        printf("- fork failed: %s -\n", strerror(errno));
+        adb_close(sv[0]);
+        adb_close(sv[1]);
+        return -1;
+    }
+
+    if (*pid == 0) {
+        adb_close(sv[0]);
+        init_subproc_child();
+
+        // Only hook up stdin/stdout; drop stderr
+        dup2(sv[1], STDIN_FILENO);
+        dup2(sv[1], STDOUT_FILENO);
+        adb_close(sv[1]);
+
+        execl(cmd, cmd, arg0, arg1, NULL);
+        fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
+                cmd, strerror(errno), errno);
+        exit(-1);
+    } else {
+        adb_close(sv[1]);
+        return sv[0];
+    }
+#endif /* !HAVE_WIN32_PROC */
+}
 #endif  /* !ABD_HOST */
 
 #if ADB_HOST
@@ -285,18 +327,32 @@
     }
 }
 
-static int create_subproc_thread(const char *name)
+static int create_subproc_thread(const char *name, const subproc_mode mode)
 {
     stinfo *sti;
     adb_thread_t t;
     int ret_fd;
-    pid_t pid;
-    if(name) {
-        ret_fd = create_subprocess(SHELL_COMMAND, "-c", name, &pid);
+    pid_t pid = -1;
+
+    const char *arg0, *arg1;
+    if (name == 0 || *name == 0) {
+        arg0 = "-"; arg1 = 0;
     } else {
-        ret_fd = create_subprocess(SHELL_COMMAND, "-", 0, &pid);
+        arg0 = "-c"; arg1 = name;
     }
-    D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid);
+
+    switch (mode) {
+    case SUBPROC_PTY:
+        ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid);
+        break;
+    case SUBPROC_RAW:
+        ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid);
+        break;
+    default:
+        fprintf(stderr, "invalid subproc_mode %d\n", mode);
+        return -1;
+    }
+    D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid);
 
     sti = malloc(sizeof(stinfo));
     if(sti == 0) fatal("cannot allocate stinfo");
@@ -304,14 +360,14 @@
     sti->cookie = (void*) (uintptr_t) pid;
     sti->fd = ret_fd;
 
-    if(adb_thread_create( &t, service_bootstrap_func, sti)){
+    if (adb_thread_create(&t, service_bootstrap_func, sti)) {
         free(sti);
         adb_close(ret_fd);
-        printf("cannot create service thread\n");
+        fprintf(stderr, "cannot create service thread\n");
         return -1;
     }
 
-    D("service thread started, fd=%d pid=%d\n",ret_fd, pid);
+    D("service thread started, fd=%d pid=%d\n", ret_fd, pid);
     return ret_fd;
 }
 #endif
@@ -356,27 +412,35 @@
     } else if (!strncmp(name, "jdwp:", 5)) {
         ret = create_jdwp_connection_fd(atoi(name+5));
     } else if(!HOST && !strncmp(name, "shell:", 6)) {
-        if(name[6]) {
-            ret = create_subproc_thread(name + 6);
-        } else {
-            ret = create_subproc_thread(0);
-        }
+        ret = create_subproc_thread(name + 6, SUBPROC_PTY);
+    } else if(!HOST && !strncmp(name, "exec:", 5)) {
+        ret = create_subproc_thread(name + 5, SUBPROC_RAW);
     } else if(!strncmp(name, "sync:", 5)) {
         ret = create_service_thread(file_sync_service, NULL);
     } else if(!strncmp(name, "remount:", 8)) {
         ret = create_service_thread(remount_service, NULL);
     } else if(!strncmp(name, "reboot:", 7)) {
         void* arg = strdup(name + 7);
-        if(arg == 0) return -1;
+        if (arg == NULL) return -1;
         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);
+        char* arg = strdup(name + 7);
         if (arg == NULL) return -1;
-        ret = backup_service(BACKUP, arg);
+        char* c = arg;
+        for (; *c != '\0'; c++) {
+            if (*c == ':')
+                *c = ' ';
+        }
+        char* cmd;
+        if (asprintf(&cmd, "/system/bin/bu backup %s", arg) != -1) {
+            ret = create_subproc_thread(cmd, SUBPROC_RAW);
+            free(cmd);
+        }
+        free(arg);
     } else if(!strncmp(name, "restore:", 8)) {
-        ret = backup_service(RESTORE, NULL);
+        ret = create_subproc_thread("/system/bin/bu restore", SUBPROC_RAW);
     } else if(!strncmp(name, "tcpip:", 6)) {
         int port;
         if (sscanf(name + 6, "%d", &port) == 0) {