Skip to content

Commit

Permalink
recovery: Provide sideload cancellation
Browse files Browse the repository at this point in the history
Change-Id: I13f0c9ae5444652a2141442ef24258679a78d320
  • Loading branch information
tdmcyngn committed Nov 25, 2015
1 parent 3381ac4 commit ffc8a87
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 93 deletions.
126 changes: 85 additions & 41 deletions adb_install.cpp
Expand Up @@ -34,6 +34,7 @@
#include "fuse_sideload.h"

static RecoveryUI* ui = NULL;
static pthread_t sideload_thread;

static void
set_usb_driver(bool enabled) {
Expand Down Expand Up @@ -66,69 +67,80 @@ maybe_restart_adbd() {
}
}

struct sideload_data {
bool* wipe_cache;
const char* install_file;
bool cancel;
int result;
};

static struct sideload_data sideload_data;

// How long (in seconds) we wait for the host to start sending us a
// package, before timing out.
#define ADB_INSTALL_TIMEOUT 300

int
apply_from_adb(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) {
modified_flash = true;

ui = ui_;

stop_adbd();
set_usb_driver(true);

ui->Print("\n\nNow send the package you want to apply\n"
"to the device with \"adb sideload <filename>\"...\n");

void *adb_sideload_thread(void* v) {
pid_t child;
if ((child = fork()) == 0) {
execl("/sbin/recovery", "recovery", "--adbd", NULL);
_exit(-1);
}

time_t start_time = time(NULL);
time_t now = start_time;

// FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host
// connects and starts serving a package. Poll for its
// appearance. (Note that inotify doesn't work with FUSE.)
int result = INSTALL_ERROR;
int status;
bool waited = false;
int result = INSTALL_NONE;
int status = -1;
struct stat st;
for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
if (waitpid(child, &status, WNOHANG) != 0) {
while (now - start_time < ADB_INSTALL_TIMEOUT) {
/*
* Exit if either:
* - The adb child process dies, or
* - The ui tells us to cancel
*/
if (kill(child, 0) != 0) {
result = INSTALL_ERROR;
waited = true;
break;
}

if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) {
sleep(1);
continue;
} else {
ui->Print("\nTimed out waiting for package.\n\n");
result = INSTALL_ERROR;
kill(child, SIGKILL);
break;
}
if (sideload_data.cancel) {
break;
}

status = stat(FUSE_SIDELOAD_HOST_PATHNAME, &st);
if (status == 0) {
break;
}
result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false);
break;
if (errno != ENOENT && errno != ENOTCONN) {
ui->Print("\nError %s waiting for package\n\n", strerror(errno));
result = INSTALL_ERROR;
break;
}

sleep(1);
now = time(NULL);
}

if (!waited) {
// Calling stat() on this magic filename signals the minadbd
// subprocess to shut down.
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
if (status == 0) {
// Signal UI thread that we can no longer cancel
ui->CancelWaitKey();

result = install_package(FUSE_SIDELOAD_HOST_PATHNAME,
sideload_data.wipe_cache,
sideload_data.install_file,
false);

// TODO(dougz): there should be a way to cancel waiting for a
// package (by pushing some button combo on the device). For now
// you just have to 'adb sideload' a file that's not a valid
// package, like "/dev/null".
waitpid(child, &status, 0);
sideload_data.result = result;
}

// Ensure adb exits
kill(child, SIGTERM);
waitpid(child, &status, 0);

if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
if (WEXITSTATUS(status) == 3) {
ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
Expand All @@ -137,8 +149,40 @@ apply_from_adb(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) {
}
}

set_usb_driver(false);
LOGI("sideload thread finished\n");
return NULL;
}

void
start_sideload(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) {
modified_flash = true;

ui = ui_;

stop_adbd();
set_usb_driver(true);

ui->Print("\n\nNow send the package you want to apply\n"
"to the device with \"adb sideload <filename>\"...\n");

sideload_data.wipe_cache = wipe_cache;
sideload_data.install_file = install_file;
sideload_data.cancel = false;
sideload_data.result = INSTALL_NONE;

pthread_create(&sideload_thread, NULL, &adb_sideload_thread, NULL);
}

void stop_sideload() {
sideload_data.cancel = true;
}

int wait_sideload() {
pthread_join(sideload_thread, NULL);

ui->FlushKeys();

maybe_restart_adbd();

return result;
return sideload_data.result;
}
4 changes: 3 additions & 1 deletion adb_install.h
Expand Up @@ -19,6 +19,8 @@

class RecoveryUI;

int apply_from_adb(RecoveryUI* h, bool* wipe_cache, const char* install_file);
void start_sideload(RecoveryUI* h, bool* wipe_cache, const char* install_file);
void stop_sideload();
int wait_sideload();

#endif
46 changes: 28 additions & 18 deletions fuse_sdcard_provider.cpp
Expand Up @@ -21,6 +21,7 @@
#include <pthread.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>

Expand Down Expand Up @@ -61,7 +62,7 @@ static void close_file(void* cookie) {
}

struct token {
pthread_t th;
pid_t pid;
const char* path;
int result;
};
Expand Down Expand Up @@ -103,19 +104,30 @@ void* start_sdcard_fuse(const char* path) {
token* t = new token;

t->path = path;
pthread_create(&(t->th), NULL, run_sdcard_fuse, t);

struct stat st;
int i;
for (i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
sleep(1);
continue;
} else {
return NULL;
}
if ((t->pid = fork()) < 0) {
free(t);
return NULL;
}
if (t->pid == 0) {
run_sdcard_fuse(t);
_exit(0);
}

time_t start_time = time(NULL);
time_t now = start_time;

while (now - start_time < SDCARD_INSTALL_TIMEOUT) {
struct stat st;
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) == 0) {
break;
}
if (errno != ENOENT && errno != ENOTCONN) {
free(t);
t = NULL;
break;
}
sleep(1);
now = time(NULL);
}

return t;
Expand All @@ -125,11 +137,9 @@ void finish_sdcard_fuse(void* cookie) {
if (cookie == NULL) return;
token* t = reinterpret_cast<token*>(cookie);

// Calling stat() on this magic filename signals the fuse
// filesystem to shut down.
struct stat st;
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
kill(t->pid, SIGTERM);
int status;
waitpid(t->pid, &status, 0);

pthread_join(t->th, NULL);
delete t;
}
39 changes: 21 additions & 18 deletions fuse_sideload.cpp
Expand Up @@ -65,10 +65,8 @@
#include "fuse_sideload.h"

#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1)
#define EXIT_FLAG_ID (FUSE_ROOT_ID+2)

#define NO_STATUS 1
#define NO_STATUS_EXIT 2

struct fuse_data {
int ffd; // file descriptor for the fuse socket
Expand Down Expand Up @@ -179,14 +177,12 @@ static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct f
fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555);
} else if (hdr->nodeid == PACKAGE_FILE_ID) {
fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
} else if (hdr->nodeid == EXIT_FLAG_ID) {
fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
} else {
return -ENOENT;
}

fuse_reply(fd, hdr->unique, &out, sizeof(out));
return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
return NO_STATUS;
}

static int handle_lookup(void* data, struct fuse_data* fd,
Expand All @@ -201,21 +197,15 @@ static int handle_lookup(void* data, struct fuse_data* fd,
out.nodeid = PACKAGE_FILE_ID;
out.generation = PACKAGE_FILE_ID;
fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
} else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, reinterpret_cast<const char*>(data),
sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) {
out.nodeid = EXIT_FLAG_ID;
out.generation = EXIT_FLAG_ID;
fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
} else {
return -ENOENT;
}

fuse_reply(fd, hdr->unique, &out, sizeof(out));
return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
return NO_STATUS;
}

static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) {
if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;

struct fuse_open_out out;
Expand Down Expand Up @@ -361,6 +351,12 @@ static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_he
return NO_STATUS;
}

static volatile int terminated = 0;
static void sig_term(int sig)
{
terminated = 1;
}

int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
uint64_t file_size, uint32_t block_size)
{
Expand Down Expand Up @@ -418,6 +414,8 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
goto done;
}

signal(SIGTERM, sig_term);

fd.ffd = open("/dev/fuse", O_RDWR);
if (fd.ffd < 0) {
perror("open /dev/fuse");
Expand All @@ -438,7 +436,17 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
goto done;
}
uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8];
for (;;) {
while (!terminated) {
fd_set fds;
struct timeval tv;
FD_ZERO(&fds);
FD_SET(fd.ffd, &fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
int rc = select(fd.ffd+1, &fds, NULL, NULL, &tv);
if (rc <= 0) {
continue;
}
ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
if (len == -1) {
perror("read request");
Expand Down Expand Up @@ -493,11 +501,6 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
break;
}

if (result == NO_STATUS_EXIT) {
result = 0;
break;
}

if (result != NO_STATUS) {
struct fuse_out_header outhdr;
outhdr.len = sizeof(outhdr);
Expand Down
2 changes: 0 additions & 2 deletions fuse_sideload.h
Expand Up @@ -21,8 +21,6 @@
#define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload"
#define FUSE_SIDELOAD_HOST_FILENAME "package.zip"
#define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME)
#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit"
#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG)

struct provider_vtab {
// read a block
Expand Down

0 comments on commit ffc8a87

Please sign in to comment.