summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Marshall <tdm@cyngn.com>2015-11-05 21:30:40 -0800
committerTom Marshall <tdm@cyngn.com>2015-11-25 15:34:35 -0800
commitffc8a8702d9e1568995ce155c648fd029909cdac (patch)
tree3dcf3d34ebb03c3ac8d140d249e55838a90e2e14
parent3381ac447175af3252c79c3e066cbbc38c400c85 (diff)
downloadbootable_recovery-ffc8a8702d9e1568995ce155c648fd029909cdac.zip
bootable_recovery-ffc8a8702d9e1568995ce155c648fd029909cdac.tar.gz
bootable_recovery-ffc8a8702d9e1568995ce155c648fd029909cdac.tar.bz2
recovery: Provide sideload cancellation
Change-Id: I13f0c9ae5444652a2141442ef24258679a78d320
-rw-r--r--adb_install.cpp126
-rw-r--r--adb_install.h4
-rw-r--r--fuse_sdcard_provider.cpp46
-rw-r--r--fuse_sideload.cpp39
-rw-r--r--fuse_sideload.h2
-rw-r--r--install.cpp21
-rw-r--r--recovery.cpp38
-rw-r--r--ui.cpp9
-rw-r--r--ui.h3
9 files changed, 195 insertions, 93 deletions
diff --git a/adb_install.cpp b/adb_install.cpp
index 4cfcb2a..a67973d 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -34,6 +34,7 @@
#include "fuse_sideload.h"
static RecoveryUI* ui = NULL;
+static pthread_t sideload_thread;
static void
set_usb_driver(bool enabled) {
@@ -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");
@@ -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;
}
diff --git a/adb_install.h b/adb_install.h
index efad436..7c9d7bc 100644
--- a/adb_install.h
+++ b/adb_install.h
@@ -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
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
index 879f90b..8681425 100644
--- a/fuse_sdcard_provider.cpp
+++ b/fuse_sdcard_provider.cpp
@@ -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>
@@ -61,7 +62,7 @@ static void close_file(void* cookie) {
}
struct token {
- pthread_t th;
+ pid_t pid;
const char* path;
int result;
};
@@ -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;
@@ -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;
}
diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp
index 9c3e75f..39c7237 100644
--- a/fuse_sideload.cpp
+++ b/fuse_sideload.cpp
@@ -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
@@ -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,
@@ -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;
@@ -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)
{
@@ -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");
@@ -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");
@@ -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);
diff --git a/fuse_sideload.h b/fuse_sideload.h
index c0b16ef..71a09a6 100644
--- a/fuse_sideload.h
+++ b/fuse_sideload.h
@@ -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
diff --git a/install.cpp b/install.cpp
index 1bdf46b..7afcff6 100644
--- a/install.cpp
+++ b/install.cpp
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <setjmp.h>
#include <sys/mount.h>
#include "common.h"
@@ -47,6 +48,12 @@ static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
+static jmp_buf jb;
+static void sig_bus(int sig)
+{
+ longjmp(jb, 1);
+}
+
// If the package contains an update binary, extract it and run it.
static int
try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) {
@@ -306,7 +313,19 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount)
ui->Print("Verifying update package...\n");
int err;
- err = verify_file(map.addr, map.length, loadedKeys, numKeys);
+
+ // Because we mmap() the update file which is backed by FUSE, we get
+ // SIGBUS when the host aborts the transfer. We handle this by using
+ // setjmp/longjmp.
+ signal(SIGBUS, sig_bus);
+ if (setjmp(jb) == 0) {
+ err = verify_file(map.addr, map.length, loadedKeys, numKeys);
+ }
+ else {
+ err = VERIFY_FAILURE;
+ }
+ signal(SIGBUS, SIG_DFL);
+
free(loadedKeys);
LOGI("verify_file returned %d\n", err);
if (err != VERIFY_SUCCESS) {
diff --git a/recovery.cpp b/recovery.cpp
index 3cd2764..5c482e0 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -616,8 +616,8 @@ get_menu_selection(const char* const * headers, const char* const * items,
ui->EndMenu();
return 0; // XXX fixme
}
- } else if (key == -2) { // we are returning from ui_cancel_wait_key(): trigger a GO_BACK
- return Device::kGoBack;
+ } else if (key == -2) { // we are returning from ui_cancel_wait_key(): no action
+ return Device::kNoAction;
}
else if (key == -6) {
return Device::kRefresh;
@@ -918,7 +918,18 @@ refresh:
break;
}
if (chosen == item_sideload) {
- status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
+ static const char* headers[] = { "ADB Sideload",
+ "",
+ NULL
+ };
+ static const char* list[] = { "Cancel sideload", NULL };
+
+ start_sideload(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
+ int item = get_menu_selection(headers, list, 0, 0, device);
+ if (item != Device::kNoAction) {
+ stop_sideload();
+ }
+ status = wait_sideload();
}
else {
std::string id = volumes[chosen - 1].mId;
@@ -991,14 +1002,16 @@ prompt_and_wait(Device* device, int status) {
}
}
- if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
- ui->Print("Installation aborted.\n");
- copy_logs();
- } else if (!ui->IsTextVisible()) {
- return Device::NO_ACTION; // reboot if logs aren't visible
- } else {
- ui->Print("\nInstall complete.\n");
+ if (status >= 0 && status != INSTALL_NONE) {
+ if (status != INSTALL_SUCCESS) {
+ ui->SetBackground(RecoveryUI::ERROR);
+ ui->Print("Installation aborted.\n");
+ copy_logs();
+ } else if (!ui->IsTextVisible()) {
+ return Device::NO_ACTION; // reboot if logs aren't visible
+ } else {
+ ui->Print("\nInstall complete.\n");
+ }
}
break;
}
@@ -1338,7 +1351,8 @@ main(int argc, char **argv) {
if (!sideload_auto_reboot) {
ui->ShowText(true);
}
- status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
+ start_sideload(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
+ status = wait_sideload();
if (status == INSTALL_SUCCESS && should_wipe_cache) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
diff --git a/ui.cpp b/ui.cpp
index 659ceb2..38a8099 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -255,6 +255,15 @@ int RecoveryUI::WaitKey() {
return key;
}
+void RecoveryUI::CancelWaitKey()
+{
+ pthread_mutex_lock(&key_queue_mutex);
+ key_queue[key_queue_len] = -2;
+ key_queue_len++;
+ pthread_cond_signal(&key_queue_cond);
+ pthread_mutex_unlock(&key_queue_mutex);
+}
+
bool RecoveryUI::IsUsbConnected() {
int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
if (fd < 0) {
diff --git a/ui.h b/ui.h
index 48eeda8..ecf221b 100644
--- a/ui.h
+++ b/ui.h
@@ -76,6 +76,9 @@ class RecoveryUI {
// Wait for a key and return it. May return -1 after timeout.
virtual int WaitKey();
+ // Cancel a WaitKey()
+ virtual void CancelWaitKey();
+
virtual bool IsKeyPressed(int key);
virtual bool IsLongPress();