summaryrefslogtreecommitdiffstats
path: root/recovery.c
diff options
context:
space:
mode:
Diffstat (limited to 'recovery.c')
-rw-r--r--recovery.c471
1 files changed, 0 insertions, 471 deletions
diff --git a/recovery.c b/recovery.c
deleted file mode 100644
index 221ee2975..000000000
--- a/recovery.c
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * 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 <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <limits.h>
-#include <linux/input.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/reboot.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "bootloader.h"
-#include "commands.h"
-#include "common.h"
-#include "cutils/properties.h"
-#include "firmware.h"
-#include "install.h"
-#include "minui/minui.h"
-#include "minzip/DirUtil.h"
-#include "roots.h"
-
-static const struct option OPTIONS[] = {
- { "send_intent", required_argument, NULL, 's' },
- { "update_package", required_argument, NULL, 'u' },
- { "wipe_data", no_argument, NULL, 'w' },
- { "wipe_cache", no_argument, NULL, 'c' },
-};
-
-static const char *COMMAND_FILE = "CACHE:recovery/command";
-static const char *INTENT_FILE = "CACHE:recovery/intent";
-static const char *LOG_FILE = "CACHE:recovery/log";
-static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
-static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
-
-/*
- * The recovery tool communicates with the main system through /cache files.
- * /cache/recovery/command - INPUT - command line for tool, one arg per line
- * /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
- * /cache/recovery/intent - OUTPUT - intent that was passed in
- *
- * The arguments which may be supplied in the recovery.command file:
- * --send_intent=anystring - write the text out to recovery.intent
- * --update_package=root:path - verify install an OTA package file
- * --wipe_data - erase user data (and cache), then reboot
- * --wipe_cache - wipe cache (but not user data), then reboot
- *
- * After completing, we remove /cache/recovery/command and reboot.
- * Arguments may also be supplied in the bootloader control block (BCB).
- * These important scenarios must be safely restartable at any point:
- *
- * FACTORY RESET
- * 1. user selects "factory reset"
- * 2. main system writes "--wipe_data" to /cache/recovery/command
- * 3. main system reboots into recovery
- * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
- * -- after this, rebooting will restart the erase --
- * 5. erase_root() reformats /data
- * 6. erase_root() reformats /cache
- * 7. finish_recovery() erases BCB
- * -- after this, rebooting will restart the main system --
- * 8. main() calls reboot() to boot main system
- *
- * OTA INSTALL
- * 1. main system downloads OTA package to /cache/some-filename.zip
- * 2. main system writes "--update_package=CACHE:some-filename.zip"
- * 3. main system reboots into recovery
- * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
- * -- after this, rebooting will attempt to reinstall the update --
- * 5. install_package() attempts to install the update
- * NOTE: the package install must itself be restartable from any point
- * 6. finish_recovery() erases BCB
- * -- after this, rebooting will (try to) restart the main system --
- * 7. ** if install failed **
- * 7a. prompt_and_wait() shows an error icon and waits for the user
- * 7b; the user reboots (pulling the battery, etc) into the main system
- * 8. main() calls maybe_install_firmware_update()
- * ** if the update contained radio/hboot firmware **:
- * 8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
- * -- after this, rebooting will reformat cache & restart main system --
- * 8b. m_i_f_u() writes firmware image into raw cache partition
- * 8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
- * -- after this, rebooting will attempt to reinstall firmware --
- * 8d. bootloader tries to flash firmware
- * 8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
- * -- after this, rebooting will reformat cache & restart main system --
- * 8f. erase_root() reformats /cache
- * 8g. finish_recovery() erases BCB
- * -- after this, rebooting will (try to) restart the main system --
- * 9. main() calls reboot() to boot main system
- */
-
-static const int MAX_ARG_LENGTH = 4096;
-static const int MAX_ARGS = 100;
-
-// open a file given in root:path format, mounting partitions as necessary
-static FILE*
-fopen_root_path(const char *root_path, const char *mode) {
- if (ensure_root_path_mounted(root_path) != 0) {
- LOGE("Can't mount %s\n", root_path);
- return NULL;
- }
-
- char path[PATH_MAX] = "";
- if (translate_root_path(root_path, path, sizeof(path)) == NULL) {
- LOGE("Bad path %s\n", root_path);
- return NULL;
- }
-
- // When writing, try to create the containing directory, if necessary.
- // Use generous permissions, the system (init.rc) will reset them.
- if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1);
-
- FILE *fp = fopen(path, mode);
- if (fp == NULL) LOGE("Can't open %s\n", path);
- return fp;
-}
-
-// close a file, log an error if the error indicator is set
-static void
-check_and_fclose(FILE *fp, const char *name) {
- fflush(fp);
- if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno));
- fclose(fp);
-}
-
-// command line args come from, in decreasing precedence:
-// - the actual command line
-// - the bootloader control block (one per line, after "recovery")
-// - the contents of COMMAND_FILE (one per line)
-static void
-get_args(int *argc, char ***argv) {
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
-
- if (boot.command[0] != 0 && boot.command[0] != 255) {
- LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
- }
-
- if (boot.status[0] != 0 && boot.status[0] != 255) {
- LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
- }
-
- // --- if arguments weren't supplied, look in the bootloader control block
- if (*argc <= 1) {
- boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
- const char *arg = strtok(boot.recovery, "\n");
- if (arg != NULL && !strcmp(arg, "recovery")) {
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = strdup(arg);
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if ((arg = strtok(NULL, "\n")) == NULL) break;
- (*argv)[*argc] = strdup(arg);
- }
- LOGI("Got arguments from boot message\n");
- } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
- LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
- }
- }
-
- // --- if that doesn't work, try the command file
- if (*argc <= 1) {
- FILE *fp = fopen_root_path(COMMAND_FILE, "r");
- if (fp != NULL) {
- char *argv0 = (*argv)[0];
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = argv0; // use the same program name
-
- char buf[MAX_ARG_LENGTH];
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if (!fgets(buf, sizeof(buf), fp)) break;
- (*argv)[*argc] = strdup(strtok(buf, "\r\n")); // Strip newline.
- }
-
- check_and_fclose(fp, COMMAND_FILE);
- LOGI("Got arguments from %s\n", COMMAND_FILE);
- }
- }
-
- // --> write the arguments we have back into the bootloader control block
- // always boot into recovery after this (until finish_recovery() is called)
- strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
- strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
- int i;
- for (i = 1; i < *argc; ++i) {
- strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
- strlcat(boot.recovery, "\n", sizeof(boot.recovery));
- }
- set_bootloader_message(&boot);
-}
-
-
-// clear the recovery command and prepare to boot a (hopefully working) system,
-// copy our log file to cache as well (for the system to read), and
-// record any intent we were asked to communicate back to the system.
-// this function is idempotent: call it as many times as you like.
-static void
-finish_recovery(const char *send_intent)
-{
- // By this point, we're ready to return to the main system...
- if (send_intent != NULL) {
- FILE *fp = fopen_root_path(INTENT_FILE, "w");
- if (fp != NULL) {
- fputs(send_intent, fp);
- check_and_fclose(fp, INTENT_FILE);
- }
- }
-
- // Copy logs to cache so the system can find out what happened.
- FILE *log = fopen_root_path(LOG_FILE, "a");
- if (log != NULL) {
- FILE *tmplog = fopen(TEMPORARY_LOG_FILE, "r");
- if (tmplog == NULL) {
- LOGE("Can't open %s\n", TEMPORARY_LOG_FILE);
- } else {
- static long tmplog_offset = 0;
- fseek(tmplog, tmplog_offset, SEEK_SET); // Since last write
- char buf[4096];
- while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
- tmplog_offset = ftell(tmplog);
- check_and_fclose(tmplog, TEMPORARY_LOG_FILE);
- }
- check_and_fclose(log, LOG_FILE);
- }
-
- // Reset the bootloader message to revert to a normal main system boot.
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- set_bootloader_message(&boot);
-
- // Remove the command file, so recovery won't repeat indefinitely.
- char path[PATH_MAX] = "";
- if (ensure_root_path_mounted(COMMAND_FILE) != 0 ||
- translate_root_path(COMMAND_FILE, path, sizeof(path)) == NULL ||
- (unlink(path) && errno != ENOENT)) {
- LOGW("Can't unlink %s\n", COMMAND_FILE);
- }
-
- sync(); // For good measure.
-}
-
-#define TEST_AMEND 0
-#if TEST_AMEND
-static void
-test_amend()
-{
- extern int test_symtab(void);
- extern int test_cmd_fn(void);
- extern int test_permissions(void);
- int ret;
- LOGD("Testing symtab...\n");
- ret = test_symtab();
- LOGD(" returned %d\n", ret);
- LOGD("Testing cmd_fn...\n");
- ret = test_cmd_fn();
- LOGD(" returned %d\n", ret);
- LOGD("Testing permissions...\n");
- ret = test_permissions();
- LOGD(" returned %d\n", ret);
-}
-#endif // TEST_AMEND
-
-static int
-erase_root(const char *root)
-{
- ui_set_background(BACKGROUND_ICON_INSTALLING);
- ui_show_indeterminate_progress();
- ui_print("Formatting %s...\n", root);
- return format_root_device(root);
-}
-
-static void
-prompt_and_wait()
-{
- char* headers[] = { "Android system recovery utility",
- "",
- "Use trackball to highlight;",
- "click to select.",
- "",
- NULL };
-
- // these constants correspond to elements of the items[] list.
-#define ITEM_REBOOT 0
-#define ITEM_APPLY_SDCARD 1
-#define ITEM_WIPE_DATA 2
- char* items[] = { "reboot system now [Home+Back]",
- "apply sdcard:update.zip [Alt+S]",
- "wipe data/factory reset [Alt+W]",
- NULL };
-
- ui_start_menu(headers, items);
- int selected = 0;
- int chosen_item = -1;
-
- finish_recovery(NULL);
- ui_reset_progress();
- for (;;) {
- int key = ui_wait_key();
- int alt = ui_key_pressed(KEY_LEFTALT) || ui_key_pressed(KEY_RIGHTALT);
- int visible = ui_text_visible();
-
- if (key == KEY_DREAM_BACK && ui_key_pressed(KEY_DREAM_HOME)) {
- // Wait for the keys to be released, to avoid triggering
- // special boot modes (like coming back into recovery!).
- while (ui_key_pressed(KEY_DREAM_BACK) ||
- ui_key_pressed(KEY_DREAM_HOME)) {
- usleep(1000);
- }
- chosen_item = ITEM_REBOOT;
- } else if (alt && key == KEY_W) {
- chosen_item = ITEM_WIPE_DATA;
- } else if (alt && key == KEY_S) {
- chosen_item = ITEM_APPLY_SDCARD;
- } else if ((key == KEY_DOWN || key == KEY_VOLUMEDOWN) && visible) {
- ++selected;
- selected = ui_menu_select(selected);
- } else if ((key == KEY_UP || key == KEY_VOLUMEUP) && visible) {
- --selected;
- selected = ui_menu_select(selected);
- } else if (key == BTN_MOUSE && visible) {
- chosen_item = selected;
- }
-
- if (chosen_item >= 0) {
- // turn off the menu, letting ui_print() to scroll output
- // on the screen.
- ui_end_menu();
-
- switch (chosen_item) {
- case ITEM_REBOOT:
- return;
-
- case ITEM_WIPE_DATA:
- ui_print("\n-- Wiping data...\n");
- erase_root("DATA:");
- erase_root("CACHE:");
- ui_print("Data wipe complete.\n");
- if (!ui_text_visible()) return;
- break;
-
- case ITEM_APPLY_SDCARD:
- ui_print("\n-- Install from sdcard...\n");
- int status = install_package(SDCARD_PACKAGE_FILE);
- if (status != INSTALL_SUCCESS) {
- ui_set_background(BACKGROUND_ICON_ERROR);
- ui_print("Installation aborted.\n");
- } else if (!ui_text_visible()) {
- return; // reboot if logs aren't visible
- } else {
- ui_print("Install from sdcard complete.\n");
- }
- break;
- }
-
- // if we didn't return from this function to reboot, show
- // the menu again.
- ui_start_menu(headers, items);
- selected = 0;
- chosen_item = -1;
-
- finish_recovery(NULL);
- ui_reset_progress();
-
- // throw away keys pressed while the command was running,
- // so user doesn't accidentally trigger menu items.
- ui_clear_key_queue();
- }
- }
-}
-
-static void
-print_property(const char *key, const char *name, void *cookie)
-{
- fprintf(stderr, "%s=%s\n", key, name);
-}
-
-int
-main(int argc, char **argv)
-{
- time_t start = time(NULL);
-
- // If these fail, there's not really anywhere to complain...
- freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
- freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
- fprintf(stderr, "Starting recovery on %s", ctime(&start));
-
- ui_init();
- get_args(&argc, &argv);
-
- int previous_runs = 0;
- const char *send_intent = NULL;
- const char *update_package = NULL;
- int wipe_data = 0, wipe_cache = 0;
-
- int arg;
- while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
- switch (arg) {
- case 'p': previous_runs = atoi(optarg); break;
- case 's': send_intent = optarg; break;
- case 'u': update_package = optarg; break;
- case 'w': wipe_data = wipe_cache = 1; break;
- case 'c': wipe_cache = 1; break;
- case '?':
- LOGE("Invalid command argument\n");
- continue;
- }
- }
-
- fprintf(stderr, "Command:");
- for (arg = 0; arg < argc; arg++) {
- fprintf(stderr, " \"%s\"", argv[arg]);
- }
- fprintf(stderr, "\n\n");
-
- property_list(print_property, NULL);
- fprintf(stderr, "\n");
-
-#if TEST_AMEND
- test_amend();
-#endif
-
- RecoveryCommandContext ctx = { NULL };
- if (register_update_commands(&ctx)) {
- LOGE("Can't install update commands\n");
- }
-
- int status = INSTALL_SUCCESS;
-
- if (update_package != NULL) {
- status = install_package(update_package);
- if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
- } else if (wipe_data || wipe_cache) {
- if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR;
- if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR;
- if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
- } else {
- status = INSTALL_ERROR; // No command specified
- }
-
- if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
- if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait();
-
- // If there is a radio image pending, reboot now to install it.
- maybe_install_firmware_update(send_intent);
-
- // Otherwise, get ready to boot the main system...
- finish_recovery(send_intent);
- ui_print("Rebooting...\n");
- sync();
- reboot(RB_AUTOBOOT);
- return EXIT_SUCCESS;
-}