Merge "Add "exec" service: shell commands with no pty."
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 2504f99..4704abb 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -326,11 +326,6 @@
int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd);
#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
@@ -418,7 +413,7 @@
# define D(...) ((void)0)
# define DR(...) ((void)0)
# define ADB_TRACING 0
-#endif
+#endif /* ADB_TRACE */
#if !DEBUG_PACKETS
@@ -476,6 +471,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 70bf641..bfc7cf8 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -287,8 +287,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;
@@ -301,9 +310,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);
}
@@ -940,7 +959,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;
@@ -977,7 +995,6 @@
int is_server = 0;
int persist = 0;
int r;
- int quote;
transport_type ttype = kTransportAny;
char* serial = NULL;
char* server_port_str = NULL;
@@ -1198,19 +1215,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(;;) {
@@ -1242,6 +1254,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 7b809da..d1e8939 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -184,11 +184,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;
@@ -215,47 +230,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
@@ -296,18 +338,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");
@@ -315,14 +371,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
@@ -367,27 +423,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) {