Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d9d434e authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

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
parent abcb9992
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -102,7 +102,6 @@ include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
	adb.c \
	backup_service.c \
	fdevent.c \
	transport.c \
	transport_local.c \
+6 −6
Original line number Diff line number Diff line
@@ -324,11 +324,6 @@ int create_jdwp_connection_fd(int jdwp_pid);
#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 @@ void adb_qemu_trace(const char* fmt, ...);
#  define  D(...)          ((void)0)
#  define  DR(...)         ((void)0)
#  define  ADB_TRACING     0
#endif
#endif /* ADB_TRACE */


#if !DEBUG_PACKETS
@@ -474,6 +469,11 @@ int connection_state(atransport *t);
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

adb/backup_service.c

deleted100644 → 0
+0 −152
Original line number Diff line number Diff line
/*
 * 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];
}
+57 −15
Original line number Diff line number Diff line
@@ -274,8 +274,17 @@ static void copy_to_file(int inFd, int outFd) {
    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 (;;) {
        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 @@ static void copy_to_file(int inFd, int outFd) {
            D("copy_to_file() : error %d\n", errno);
            break;
        }
        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 @@ static const char *find_product_out_path(const char *hint)
    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 adb_commandline(int argc, char **argv)
    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 @@ top:
            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, "\"");
            char *quoted = dupAndQuote(*argv++);
            strncat(buf, " ", sizeof(buf) - 1);
            strncat(buf, quoted, sizeof(buf) - 1);
            free(quoted);
        }

        for(;;) {
@@ -1229,6 +1241,36 @@ top:
        }
    }

    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");
+108 −44
Original line number Diff line number Diff line
@@ -173,11 +173,26 @@ static int create_service_thread(void (*func)(int, void *), void *cookie)
}

#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;
@@ -205,43 +220,70 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1
    }

    if (*pid == 0) {
        int pts;
        init_subproc_child();

        setsid();

        pts = unix_open(devname, O_RDWR);
        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);
        execl(cmd, cmd, arg0, arg1, NULL);
        fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
                cmd, strerror(errno), errno);
        exit(-1);
    } else {
           D("adb: unable to open %s\n", text);
        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 {
        // 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;
        adb_close(sv[1]);
        return sv[0];
    }
#endif /* !HAVE_WIN32_PROC */
}
@@ -285,18 +327,32 @@ static void subproc_waiter_service(int fd, void *cookie)
    }
}

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");
@@ -307,7 +363,7 @@ static int create_subproc_thread(const char *name)
    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;
    }

@@ -356,27 +412,35 @@ int service_to_fd(const char *name)
    } 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);
        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) {