platform_system_core/adb/services.c
Mike Lockwood 535164e9d9 Reimplement the "adb root" command to more closely match its previous behavior
The adb gadget driver used to reset the USB bus when the adbd daemon exited,
and the host side adb relied on this behavior to force it to reconnect
with the new adbd instance after init relaunches it.  The new gadget
drivers no longer automatically reset the USB bus when adbd is restarted
which caused adb to hang since it was no longer forced to reconnect with the
device.  We attempted to work around this on the host side adb, but that
work around has not been reliable.

This change adds a property trigger on the service.adb.root system property
which will reset the USB bus and restart the adbd daemon when adbd sets
the property to 1.  This should be much closer to the previous behavior
and will hopefully fix some problems with automated testing.

Change-Id: I177c37400009a3d83f21a5f9431f94fd1cc19b9b
Signed-off-by: Mike Lockwood <lockwood@android.com>
2011-06-22 08:36:53 -04:00

569 lines
15 KiB
C

/*
* Copyright (C) 2007 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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_SERVICES
#include "adb.h"
#include "file_sync_service.h"
#if ADB_HOST
# ifndef HAVE_WINSOCK
# include <netinet/in.h>
# include <netdb.h>
# include <sys/ioctl.h>
# endif
#else
# include <cutils/android_reboot.h>
#endif
typedef struct stinfo stinfo;
struct stinfo {
void (*func)(int fd, void *cookie);
int fd;
void *cookie;
};
void *service_bootstrap_func(void *x)
{
stinfo *sti = x;
sti->func(sti->fd, sti->cookie);
free(sti);
return 0;
}
#if ADB_HOST
ADB_MUTEX_DEFINE( dns_lock );
static void dns_service(int fd, void *cookie)
{
char *hostname = cookie;
struct hostent *hp;
unsigned zero = 0;
adb_mutex_lock(&dns_lock);
hp = gethostbyname(hostname);
free(cookie);
if(hp == 0) {
writex(fd, &zero, 4);
} else {
writex(fd, hp->h_addr, 4);
}
adb_mutex_unlock(&dns_lock);
adb_close(fd);
}
#else
extern int recovery_mode;
static void recover_service(int s, void *cookie)
{
unsigned char buf[4096];
unsigned count = (unsigned) cookie;
int fd;
fd = adb_creat("/tmp/update", 0644);
if(fd < 0) {
adb_close(s);
return;
}
while(count > 0) {
unsigned xfer = (count > 4096) ? 4096 : count;
if(readx(s, buf, xfer)) break;
if(writex(fd, buf, xfer)) break;
count -= xfer;
}
if(count == 0) {
writex(s, "OKAY", 4);
} else {
writex(s, "FAIL", 4);
}
adb_close(fd);
adb_close(s);
fd = adb_creat("/tmp/update.begin", 0644);
adb_close(fd);
}
void restart_root_service(int fd, void *cookie)
{
char buf[100];
char value[PROPERTY_VALUE_MAX];
if (getuid() == 0) {
snprintf(buf, sizeof(buf), "adbd is already running as root\n");
writex(fd, buf, strlen(buf));
adb_close(fd);
} else {
property_get("ro.debuggable", value, "");
if (strcmp(value, "1") != 0) {
snprintf(buf, sizeof(buf), "adbd cannot run as root in production builds\n");
writex(fd, buf, strlen(buf));
adb_close(fd);
return;
}
snprintf(buf, sizeof(buf), "restarting adbd as root\n");
writex(fd, buf, strlen(buf));
adb_close(fd);
// This will cause a property trigger in init.rc to restart us
property_set("service.adb.root", "1");
}
}
void restart_tcp_service(int fd, void *cookie)
{
char buf[100];
char value[PROPERTY_VALUE_MAX];
int port = (int)cookie;
if (port <= 0) {
snprintf(buf, sizeof(buf), "invalid port\n");
writex(fd, buf, strlen(buf));
adb_close(fd);
return;
}
snprintf(value, sizeof(value), "%d", port);
property_set("service.adb.tcp.port", value);
snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port);
writex(fd, buf, strlen(buf));
adb_close(fd);
// quit, and init will restart us in TCP mode
sleep(1);
exit(1);
}
void restart_usb_service(int fd, void *cookie)
{
char buf[100];
property_set("service.adb.tcp.port", "0");
snprintf(buf, sizeof(buf), "restarting in USB mode\n");
writex(fd, buf, strlen(buf));
adb_close(fd);
// quit, and init will restart us in USB mode
sleep(1);
exit(1);
}
void reboot_service(int fd, void *arg)
{
char buf[100];
int pid, ret;
sync();
/* Attempt to unmount the SD card first.
* No need to bother checking for errors.
*/
pid = fork();
if (pid == 0) {
/* ask vdc to unmount it */
execl("/system/bin/vdc", "/system/bin/vdc", "volume", "unmount",
getenv("EXTERNAL_STORAGE"), "force", NULL);
} else if (pid > 0) {
/* wait until vdc succeeds or fails */
waitpid(pid, &ret, 0);
}
ret = android_reboot(ANDROID_RB_RESTART2, 0, (char *) arg);
if (ret < 0) {
snprintf(buf, sizeof(buf), "reboot failed: %s\n", strerror(errno));
writex(fd, buf, strlen(buf));
}
free(arg);
adb_close(fd);
}
#endif
#if 0
static void echo_service(int fd, void *cookie)
{
char buf[4096];
int r;
char *p;
int c;
for(;;) {
r = read(fd, buf, 4096);
if(r == 0) goto done;
if(r < 0) {
if(errno == EINTR) continue;
else goto done;
}
c = r;
p = buf;
while(c > 0) {
r = write(fd, p, c);
if(r > 0) {
c -= r;
p += r;
continue;
}
if((r < 0) && (errno == EINTR)) continue;
goto done;
}
}
done:
close(fd);
}
#endif
static int create_service_thread(void (*func)(int, void *), void *cookie)
{
stinfo *sti;
adb_thread_t t;
int s[2];
if(adb_socketpair(s)) {
printf("cannot create service socket pair\n");
return -1;
}
sti = malloc(sizeof(stinfo));
if(sti == 0) fatal("cannot allocate stinfo");
sti->func = func;
sti->cookie = cookie;
sti->fd = s[1];
if(adb_thread_create( &t, service_bootstrap_func, sti)){
free(sti);
adb_close(s[0]);
adb_close(s[1]);
printf("cannot create service thread\n");
return -1;
}
D("service thread started, %d:%d\n",s[0], s[1]);
return s[0];
}
#if !ADB_HOST
static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
{
#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);
return -1;
#else /* !HAVE_WIN32_PROC */
char *devname;
int ptm;
ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
if(ptm < 0){
printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
return -1;
}
fcntl(ptm, F_SETFD, FD_CLOEXEC);
if(grantpt(ptm) || unlockpt(ptm) ||
((devname = (char*) ptsname(ptm)) == 0)){
printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
adb_close(ptm);
return -1;
}
*pid = fork();
if(*pid < 0) {
printf("- fork failed: %s -\n", strerror(errno));
adb_close(ptm);
return -1;
}
if(*pid == 0){
int pts;
setsid();
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);
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 */
}
#endif /* !ABD_HOST */
#if ADB_HOST
#define SHELL_COMMAND "/bin/sh"
#else
#define SHELL_COMMAND "/system/bin/sh"
#endif
#if !ADB_HOST
static void subproc_waiter_service(int fd, void *cookie)
{
pid_t pid = (pid_t)cookie;
D("entered. fd=%d of pid=%d\n", fd, pid);
for (;;) {
int status;
pid_t p = waitpid(pid, &status, 0);
if (p == pid) {
D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status);
if (WIFSIGNALED(status)) {
D("*** Killed by signal %d\n", WTERMSIG(status));
break;
} else if (!WIFEXITED(status)) {
D("*** Didn't exit!!. status %d\n", status);
break;
} else if (WEXITSTATUS(status) >= 0) {
D("*** Exit code %d\n", WEXITSTATUS(status));
break;
}
}
usleep(100000); // poll every 0.1 sec
}
D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno);
if (SHELL_EXIT_NOTIFY_FD >=0) {
int res;
res = writex(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd));
D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n",
SHELL_EXIT_NOTIFY_FD, pid, res, errno);
}
}
static int create_subproc_thread(const char *name)
{
stinfo *sti;
adb_thread_t t;
int ret_fd;
pid_t pid;
if(name) {
ret_fd = create_subprocess(SHELL_COMMAND, "-c", name, &pid);
} else {
ret_fd = create_subprocess(SHELL_COMMAND, "-", 0, &pid);
}
D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid);
sti = malloc(sizeof(stinfo));
if(sti == 0) fatal("cannot allocate stinfo");
sti->func = subproc_waiter_service;
sti->cookie = (void*)pid;
sti->fd = ret_fd;
if(adb_thread_create( &t, service_bootstrap_func, sti)){
free(sti);
adb_close(ret_fd);
printf("cannot create service thread\n");
return -1;
}
D("service thread started, fd=%d pid=%d\n",ret_fd, pid);
return ret_fd;
}
#endif
int service_to_fd(const char *name)
{
int ret = -1;
if(!strncmp(name, "tcp:", 4)) {
int port = atoi(name + 4);
name = strchr(name + 4, ':');
if(name == 0) {
ret = socket_loopback_client(port, SOCK_STREAM);
if (ret >= 0)
disable_tcp_nagle(ret);
} else {
#if ADB_HOST
adb_mutex_lock(&dns_lock);
ret = socket_network_client(name + 1, port, SOCK_STREAM);
adb_mutex_unlock(&dns_lock);
#else
return -1;
#endif
}
#ifndef HAVE_WINSOCK /* winsock doesn't implement unix domain sockets */
} else if(!strncmp(name, "local:", 6)) {
ret = socket_local_client(name + 6,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
} else if(!strncmp(name, "localreserved:", 14)) {
ret = socket_local_client(name + 14,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
} else if(!strncmp(name, "localabstract:", 14)) {
ret = socket_local_client(name + 14,
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
} else if(!strncmp(name, "localfilesystem:", 16)) {
ret = socket_local_client(name + 16,
ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
#endif
#if ADB_HOST
} else if(!strncmp("dns:", name, 4)){
char *n = strdup(name + 4);
if(n == 0) return -1;
ret = create_service_thread(dns_service, n);
#else /* !ADB_HOST */
} else if(!strncmp("dev:", name, 4)) {
ret = unix_open(name + 4, O_RDWR);
} else if(!strncmp(name, "framebuffer:", 12)) {
ret = create_service_thread(framebuffer_service, 0);
} else if(recovery_mode && !strncmp(name, "recover:", 8)) {
ret = create_service_thread(recover_service, (void*) atoi(name + 8));
} else if (!strncmp(name, "jdwp:", 5)) {
ret = create_jdwp_connection_fd(atoi(name+5));
} else if (!strncmp(name, "log:", 4)) {
ret = create_service_thread(log_service, get_log_file_path(name + 4));
} else if(!HOST && !strncmp(name, "shell:", 6)) {
if(name[6]) {
ret = create_subproc_thread(name + 6);
} else {
ret = create_subproc_thread(0);
}
} 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;
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);
} else if(!strncmp(name, "restore:", 8)) {
ret = backup_service(RESTORE, NULL);
} else if(!strncmp(name, "tcpip:", 6)) {
int port;
if (sscanf(name + 6, "%d", &port) == 0) {
port = 0;
}
ret = create_service_thread(restart_tcp_service, (void *)port);
} else if(!strncmp(name, "usb:", 4)) {
ret = create_service_thread(restart_usb_service, NULL);
#endif
#if 0
} else if(!strncmp(name, "echo:", 5)){
ret = create_service_thread(echo_service, 0);
#endif
}
if (ret >= 0) {
close_on_exec(ret);
}
return ret;
}
#if ADB_HOST
struct state_info {
transport_type transport;
char* serial;
int state;
};
static void wait_for_state(int fd, void* cookie)
{
struct state_info* sinfo = cookie;
char* err = "unknown error";
D("wait_for_state %d\n", sinfo->state);
atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err);
if(t != 0) {
writex(fd, "OKAY", 4);
} else {
sendfailmsg(fd, err);
}
if (sinfo->serial)
free(sinfo->serial);
free(sinfo);
adb_close(fd);
D("wait_for_state is done\n");
}
#endif
#if ADB_HOST
asocket* host_service_to_socket(const char* name, const char *serial)
{
if (!strcmp(name,"track-devices")) {
return create_device_tracker();
} else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
struct state_info* sinfo = malloc(sizeof(struct state_info));
if (serial)
sinfo->serial = strdup(serial);
else
sinfo->serial = NULL;
name += strlen("wait-for-");
if (!strncmp(name, "local", strlen("local"))) {
sinfo->transport = kTransportLocal;
sinfo->state = CS_DEVICE;
} else if (!strncmp(name, "usb", strlen("usb"))) {
sinfo->transport = kTransportUsb;
sinfo->state = CS_DEVICE;
} else if (!strncmp(name, "any", strlen("any"))) {
sinfo->transport = kTransportAny;
sinfo->state = CS_DEVICE;
} else {
free(sinfo);
return NULL;
}
int fd = create_service_thread(wait_for_state, sinfo);
return create_local_socket(fd);
}
return NULL;
}
#endif /* ADB_HOST */