recovery.cpp revision 8b240ccca1ad32cbd09d3807614f3086914ceaaf
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <ctype.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <getopt.h>
21#include <limits.h>
22#include <linux/input.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <time.h>
29#include <unistd.h>
30#include <dirent.h>
31
32#include "bootloader.h"
33#include "common.h"
34#include "cutils/properties.h"
35#include "cutils/android_reboot.h"
36#include "install.h"
37#include "minui/minui.h"
38#include "minzip/DirUtil.h"
39#include "roots.h"
40#include "ui.h"
41#include "screen_ui.h"
42#include "device.h"
43#include "adb_install.h"
44extern "C" {
45#include "minadbd/adb.h"
46}
47
48struct selabel_handle *sehandle;
49
50static const struct option OPTIONS[] = {
51  { "send_intent", required_argument, NULL, 's' },
52  { "update_package", required_argument, NULL, 'u' },
53  { "wipe_data", no_argument, NULL, 'w' },
54  { "wipe_cache", no_argument, NULL, 'c' },
55  { "show_text", no_argument, NULL, 't' },
56  { "just_exit", no_argument, NULL, 'x' },
57  { "locale", required_argument, NULL, 'l' },
58  { NULL, 0, NULL, 0 },
59};
60
61static const char *COMMAND_FILE = "/cache/recovery/command";
62static const char *INTENT_FILE = "/cache/recovery/intent";
63static const char *LOG_FILE = "/cache/recovery/log";
64static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
65static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
66static const char *LOCALE_FILE = "/cache/recovery/last_locale";
67static const char *CACHE_ROOT = "/cache";
68static const char *SDCARD_ROOT = "/sdcard";
69static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
70static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
71static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
72
73RecoveryUI* ui = NULL;
74char* locale = NULL;
75
76/*
77 * The recovery tool communicates with the main system through /cache files.
78 *   /cache/recovery/command - INPUT - command line for tool, one arg per line
79 *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
80 *   /cache/recovery/intent - OUTPUT - intent that was passed in
81 *
82 * The arguments which may be supplied in the recovery.command file:
83 *   --send_intent=anystring - write the text out to recovery.intent
84 *   --update_package=path - verify install an OTA package file
85 *   --wipe_data - erase user data (and cache), then reboot
86 *   --wipe_cache - wipe cache (but not user data), then reboot
87 *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
88 *   --just_exit - do nothing; exit and reboot
89 *
90 * After completing, we remove /cache/recovery/command and reboot.
91 * Arguments may also be supplied in the bootloader control block (BCB).
92 * These important scenarios must be safely restartable at any point:
93 *
94 * FACTORY RESET
95 * 1. user selects "factory reset"
96 * 2. main system writes "--wipe_data" to /cache/recovery/command
97 * 3. main system reboots into recovery
98 * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
99 *    -- after this, rebooting will restart the erase --
100 * 5. erase_volume() reformats /data
101 * 6. erase_volume() reformats /cache
102 * 7. finish_recovery() erases BCB
103 *    -- after this, rebooting will restart the main system --
104 * 8. main() calls reboot() to boot main system
105 *
106 * OTA INSTALL
107 * 1. main system downloads OTA package to /cache/some-filename.zip
108 * 2. main system writes "--update_package=/cache/some-filename.zip"
109 * 3. main system reboots into recovery
110 * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
111 *    -- after this, rebooting will attempt to reinstall the update --
112 * 5. install_package() attempts to install the update
113 *    NOTE: the package install must itself be restartable from any point
114 * 6. finish_recovery() erases BCB
115 *    -- after this, rebooting will (try to) restart the main system --
116 * 7. ** if install failed **
117 *    7a. prompt_and_wait() shows an error icon and waits for the user
118 *    7b; the user reboots (pulling the battery, etc) into the main system
119 * 8. main() calls maybe_install_firmware_update()
120 *    ** if the update contained radio/hboot firmware **:
121 *    8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
122 *        -- after this, rebooting will reformat cache & restart main system --
123 *    8b. m_i_f_u() writes firmware image into raw cache partition
124 *    8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
125 *        -- after this, rebooting will attempt to reinstall firmware --
126 *    8d. bootloader tries to flash firmware
127 *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
128 *        -- after this, rebooting will reformat cache & restart main system --
129 *    8f. erase_volume() reformats /cache
130 *    8g. finish_recovery() erases BCB
131 *        -- after this, rebooting will (try to) restart the main system --
132 * 9. main() calls reboot() to boot main system
133 */
134
135static const int MAX_ARG_LENGTH = 4096;
136static const int MAX_ARGS = 100;
137
138// open a given path, mounting partitions as necessary
139FILE*
140fopen_path(const char *path, const char *mode) {
141    if (ensure_path_mounted(path) != 0) {
142        LOGE("Can't mount %s\n", path);
143        return NULL;
144    }
145
146    // When writing, try to create the containing directory, if necessary.
147    // Use generous permissions, the system (init.rc) will reset them.
148    if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1, sehandle);
149
150    FILE *fp = fopen(path, mode);
151    return fp;
152}
153
154// close a file, log an error if the error indicator is set
155static void
156check_and_fclose(FILE *fp, const char *name) {
157    fflush(fp);
158    if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno));
159    fclose(fp);
160}
161
162// command line args come from, in decreasing precedence:
163//   - the actual command line
164//   - the bootloader control block (one per line, after "recovery")
165//   - the contents of COMMAND_FILE (one per line)
166static void
167get_args(int *argc, char ***argv) {
168    struct bootloader_message boot;
169    memset(&boot, 0, sizeof(boot));
170    get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure
171
172    if (boot.command[0] != 0 && boot.command[0] != 255) {
173        LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
174    }
175
176    if (boot.status[0] != 0 && boot.status[0] != 255) {
177        LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
178    }
179
180    // --- if arguments weren't supplied, look in the bootloader control block
181    if (*argc <= 1) {
182        boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
183        const char *arg = strtok(boot.recovery, "\n");
184        if (arg != NULL && !strcmp(arg, "recovery")) {
185            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
186            (*argv)[0] = strdup(arg);
187            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
188                if ((arg = strtok(NULL, "\n")) == NULL) break;
189                (*argv)[*argc] = strdup(arg);
190            }
191            LOGI("Got arguments from boot message\n");
192        } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
193            LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
194        }
195    }
196
197    // --- if that doesn't work, try the command file
198    if (*argc <= 1) {
199        FILE *fp = fopen_path(COMMAND_FILE, "r");
200        if (fp != NULL) {
201            char *argv0 = (*argv)[0];
202            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
203            (*argv)[0] = argv0;  // use the same program name
204
205            char buf[MAX_ARG_LENGTH];
206            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
207                if (!fgets(buf, sizeof(buf), fp)) break;
208                (*argv)[*argc] = strdup(strtok(buf, "\r\n"));  // Strip newline.
209            }
210
211            check_and_fclose(fp, COMMAND_FILE);
212            LOGI("Got arguments from %s\n", COMMAND_FILE);
213        }
214    }
215
216    // --> write the arguments we have back into the bootloader control block
217    // always boot into recovery after this (until finish_recovery() is called)
218    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
219    strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
220    int i;
221    for (i = 1; i < *argc; ++i) {
222        strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
223        strlcat(boot.recovery, "\n", sizeof(boot.recovery));
224    }
225    set_bootloader_message(&boot);
226}
227
228static void
229set_sdcard_update_bootloader_message() {
230    struct bootloader_message boot;
231    memset(&boot, 0, sizeof(boot));
232    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
233    strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
234    set_bootloader_message(&boot);
235}
236
237// How much of the temp log we have copied to the copy in cache.
238static long tmplog_offset = 0;
239
240static void
241copy_log_file(const char* source, const char* destination, int append) {
242    FILE *log = fopen_path(destination, append ? "a" : "w");
243    if (log == NULL) {
244        LOGE("Can't open %s\n", destination);
245    } else {
246        FILE *tmplog = fopen(source, "r");
247        if (tmplog != NULL) {
248            if (append) {
249                fseek(tmplog, tmplog_offset, SEEK_SET);  // Since last write
250            }
251            char buf[4096];
252            while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
253            if (append) {
254                tmplog_offset = ftell(tmplog);
255            }
256            check_and_fclose(tmplog, source);
257        }
258        check_and_fclose(log, destination);
259    }
260}
261
262
263// clear the recovery command and prepare to boot a (hopefully working) system,
264// copy our log file to cache as well (for the system to read), and
265// record any intent we were asked to communicate back to the system.
266// this function is idempotent: call it as many times as you like.
267static void
268finish_recovery(const char *send_intent) {
269    // By this point, we're ready to return to the main system...
270    if (send_intent != NULL) {
271        FILE *fp = fopen_path(INTENT_FILE, "w");
272        if (fp == NULL) {
273            LOGE("Can't open %s\n", INTENT_FILE);
274        } else {
275            fputs(send_intent, fp);
276            check_and_fclose(fp, INTENT_FILE);
277        }
278    }
279
280    // Save the locale to cache, so if recovery is next started up
281    // without a --locale argument (eg, directly from the bootloader)
282    // it will use the last-known locale.
283    if (locale != NULL) {
284        LOGI("Saving locale \"%s\"\n", locale);
285        FILE* fp = fopen_path(LOCALE_FILE, "w");
286        fwrite(locale, 1, strlen(locale), fp);
287        fflush(fp);
288        fsync(fileno(fp));
289        check_and_fclose(fp, LOCALE_FILE);
290    }
291
292    // Copy logs to cache so the system can find out what happened.
293    copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);
294    copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false);
295    copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false);
296    chmod(LOG_FILE, 0600);
297    chown(LOG_FILE, 1000, 1000);   // system user
298    chmod(LAST_LOG_FILE, 0640);
299    chmod(LAST_INSTALL_FILE, 0644);
300
301    // Reset to normal system boot so recovery won't cycle indefinitely.
302    struct bootloader_message boot;
303    memset(&boot, 0, sizeof(boot));
304    set_bootloader_message(&boot);
305
306    // Remove the command file, so recovery won't repeat indefinitely.
307    if (ensure_path_mounted(COMMAND_FILE) != 0 ||
308        (unlink(COMMAND_FILE) && errno != ENOENT)) {
309        LOGW("Can't unlink %s\n", COMMAND_FILE);
310    }
311
312    ensure_path_unmounted(CACHE_ROOT);
313    sync();  // For good measure.
314}
315
316static int
317erase_volume(const char *volume) {
318    ui->SetBackground(RecoveryUI::ERASING);
319    ui->SetProgressType(RecoveryUI::INDETERMINATE);
320    ui->Print("Formatting %s...\n", volume);
321
322    ensure_path_unmounted(volume);
323
324    if (strcmp(volume, "/cache") == 0) {
325        // Any part of the log we'd copied to cache is now gone.
326        // Reset the pointer so we copy from the beginning of the temp
327        // log.
328        tmplog_offset = 0;
329    }
330
331    return format_volume(volume);
332}
333
334static char*
335copy_sideloaded_package(const char* original_path) {
336  if (ensure_path_mounted(original_path) != 0) {
337    LOGE("Can't mount %s\n", original_path);
338    return NULL;
339  }
340
341  if (ensure_path_mounted(SIDELOAD_TEMP_DIR) != 0) {
342    LOGE("Can't mount %s\n", SIDELOAD_TEMP_DIR);
343    return NULL;
344  }
345
346  if (mkdir(SIDELOAD_TEMP_DIR, 0700) != 0) {
347    if (errno != EEXIST) {
348      LOGE("Can't mkdir %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
349      return NULL;
350    }
351  }
352
353  // verify that SIDELOAD_TEMP_DIR is exactly what we expect: a
354  // directory, owned by root, readable and writable only by root.
355  struct stat st;
356  if (stat(SIDELOAD_TEMP_DIR, &st) != 0) {
357    LOGE("failed to stat %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
358    return NULL;
359  }
360  if (!S_ISDIR(st.st_mode)) {
361    LOGE("%s isn't a directory\n", SIDELOAD_TEMP_DIR);
362    return NULL;
363  }
364  if ((st.st_mode & 0777) != 0700) {
365    LOGE("%s has perms %o\n", SIDELOAD_TEMP_DIR, st.st_mode);
366    return NULL;
367  }
368  if (st.st_uid != 0) {
369    LOGE("%s owned by %lu; not root\n", SIDELOAD_TEMP_DIR, st.st_uid);
370    return NULL;
371  }
372
373  char copy_path[PATH_MAX];
374  strcpy(copy_path, SIDELOAD_TEMP_DIR);
375  strcat(copy_path, "/package.zip");
376
377  char* buffer = (char*)malloc(BUFSIZ);
378  if (buffer == NULL) {
379    LOGE("Failed to allocate buffer\n");
380    return NULL;
381  }
382
383  size_t read;
384  FILE* fin = fopen(original_path, "rb");
385  if (fin == NULL) {
386    LOGE("Failed to open %s (%s)\n", original_path, strerror(errno));
387    return NULL;
388  }
389  FILE* fout = fopen(copy_path, "wb");
390  if (fout == NULL) {
391    LOGE("Failed to open %s (%s)\n", copy_path, strerror(errno));
392    return NULL;
393  }
394
395  while ((read = fread(buffer, 1, BUFSIZ, fin)) > 0) {
396    if (fwrite(buffer, 1, read, fout) != read) {
397      LOGE("Short write of %s (%s)\n", copy_path, strerror(errno));
398      return NULL;
399    }
400  }
401
402  free(buffer);
403
404  if (fclose(fout) != 0) {
405    LOGE("Failed to close %s (%s)\n", copy_path, strerror(errno));
406    return NULL;
407  }
408
409  if (fclose(fin) != 0) {
410    LOGE("Failed to close %s (%s)\n", original_path, strerror(errno));
411    return NULL;
412  }
413
414  // "adb push" is happy to overwrite read-only files when it's
415  // running as root, but we'll try anyway.
416  if (chmod(copy_path, 0400) != 0) {
417    LOGE("Failed to chmod %s (%s)\n", copy_path, strerror(errno));
418    return NULL;
419  }
420
421  return strdup(copy_path);
422}
423
424static const char**
425prepend_title(const char* const* headers) {
426    const char* title[] = { "Android system recovery <"
427                            EXPAND(RECOVERY_API_VERSION) "e>",
428                            "",
429                            NULL };
430
431    // count the number of lines in our title, plus the
432    // caller-provided headers.
433    int count = 0;
434    const char* const* p;
435    for (p = title; *p; ++p, ++count);
436    for (p = headers; *p; ++p, ++count);
437
438    const char** new_headers = (const char**)malloc((count+1) * sizeof(char*));
439    const char** h = new_headers;
440    for (p = title; *p; ++p, ++h) *h = *p;
441    for (p = headers; *p; ++p, ++h) *h = *p;
442    *h = NULL;
443
444    return new_headers;
445}
446
447static int
448get_menu_selection(const char* const * headers, const char* const * items,
449                   int menu_only, int initial_selection, Device* device) {
450    // throw away keys pressed previously, so user doesn't
451    // accidentally trigger menu items.
452    ui->FlushKeys();
453
454    ui->StartMenu(headers, items, initial_selection);
455    int selected = initial_selection;
456    int chosen_item = -1;
457
458    while (chosen_item < 0) {
459        int key = ui->WaitKey();
460        int visible = ui->IsTextVisible();
461
462        if (key == -1) {   // ui_wait_key() timed out
463            if (ui->WasTextEverVisible()) {
464                continue;
465            } else {
466                LOGI("timed out waiting for key input; rebooting.\n");
467                ui->EndMenu();
468                return 0; // XXX fixme
469            }
470        }
471
472        int action = device->HandleMenuKey(key, visible);
473
474        if (action < 0) {
475            switch (action) {
476                case Device::kHighlightUp:
477                    --selected;
478                    selected = ui->SelectMenu(selected);
479                    break;
480                case Device::kHighlightDown:
481                    ++selected;
482                    selected = ui->SelectMenu(selected);
483                    break;
484                case Device::kInvokeItem:
485                    chosen_item = selected;
486                    break;
487                case Device::kNoAction:
488                    break;
489            }
490        } else if (!menu_only) {
491            chosen_item = action;
492        }
493    }
494
495    ui->EndMenu();
496    return chosen_item;
497}
498
499static int compare_string(const void* a, const void* b) {
500    return strcmp(*(const char**)a, *(const char**)b);
501}
502
503static int
504update_directory(const char* path, const char* unmount_when_done,
505                 int* wipe_cache, Device* device) {
506    ensure_path_mounted(path);
507
508    const char* MENU_HEADERS[] = { "Choose a package to install:",
509                                   path,
510                                   "",
511                                   NULL };
512    DIR* d;
513    struct dirent* de;
514    d = opendir(path);
515    if (d == NULL) {
516        LOGE("error opening %s: %s\n", path, strerror(errno));
517        if (unmount_when_done != NULL) {
518            ensure_path_unmounted(unmount_when_done);
519        }
520        return 0;
521    }
522
523    const char** headers = prepend_title(MENU_HEADERS);
524
525    int d_size = 0;
526    int d_alloc = 10;
527    char** dirs = (char**)malloc(d_alloc * sizeof(char*));
528    int z_size = 1;
529    int z_alloc = 10;
530    char** zips = (char**)malloc(z_alloc * sizeof(char*));
531    zips[0] = strdup("../");
532
533    while ((de = readdir(d)) != NULL) {
534        int name_len = strlen(de->d_name);
535
536        if (de->d_type == DT_DIR) {
537            // skip "." and ".." entries
538            if (name_len == 1 && de->d_name[0] == '.') continue;
539            if (name_len == 2 && de->d_name[0] == '.' &&
540                de->d_name[1] == '.') continue;
541
542            if (d_size >= d_alloc) {
543                d_alloc *= 2;
544                dirs = (char**)realloc(dirs, d_alloc * sizeof(char*));
545            }
546            dirs[d_size] = (char*)malloc(name_len + 2);
547            strcpy(dirs[d_size], de->d_name);
548            dirs[d_size][name_len] = '/';
549            dirs[d_size][name_len+1] = '\0';
550            ++d_size;
551        } else if (de->d_type == DT_REG &&
552                   name_len >= 4 &&
553                   strncasecmp(de->d_name + (name_len-4), ".zip", 4) == 0) {
554            if (z_size >= z_alloc) {
555                z_alloc *= 2;
556                zips = (char**)realloc(zips, z_alloc * sizeof(char*));
557            }
558            zips[z_size++] = strdup(de->d_name);
559        }
560    }
561    closedir(d);
562
563    qsort(dirs, d_size, sizeof(char*), compare_string);
564    qsort(zips, z_size, sizeof(char*), compare_string);
565
566    // append dirs to the zips list
567    if (d_size + z_size + 1 > z_alloc) {
568        z_alloc = d_size + z_size + 1;
569        zips = (char**)realloc(zips, z_alloc * sizeof(char*));
570    }
571    memcpy(zips + z_size, dirs, d_size * sizeof(char*));
572    free(dirs);
573    z_size += d_size;
574    zips[z_size] = NULL;
575
576    int result;
577    int chosen_item = 0;
578    do {
579        chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device);
580
581        char* item = zips[chosen_item];
582        int item_len = strlen(item);
583        if (chosen_item == 0) {          // item 0 is always "../"
584            // go up but continue browsing (if the caller is update_directory)
585            result = -1;
586            break;
587        } else if (item[item_len-1] == '/') {
588            // recurse down into a subdirectory
589            char new_path[PATH_MAX];
590            strlcpy(new_path, path, PATH_MAX);
591            strlcat(new_path, "/", PATH_MAX);
592            strlcat(new_path, item, PATH_MAX);
593            new_path[strlen(new_path)-1] = '\0';  // truncate the trailing '/'
594            result = update_directory(new_path, unmount_when_done, wipe_cache, device);
595            if (result >= 0) break;
596        } else {
597            // selected a zip file:  attempt to install it, and return
598            // the status to the caller.
599            char new_path[PATH_MAX];
600            strlcpy(new_path, path, PATH_MAX);
601            strlcat(new_path, "/", PATH_MAX);
602            strlcat(new_path, item, PATH_MAX);
603
604            ui->Print("\n-- Install %s ...\n", path);
605            set_sdcard_update_bootloader_message();
606            char* copy = copy_sideloaded_package(new_path);
607            if (unmount_when_done != NULL) {
608                ensure_path_unmounted(unmount_when_done);
609            }
610            if (copy) {
611                result = install_package(copy, wipe_cache, TEMPORARY_INSTALL_FILE);
612                free(copy);
613            } else {
614                result = INSTALL_ERROR;
615            }
616            break;
617        }
618    } while (true);
619
620    int i;
621    for (i = 0; i < z_size; ++i) free(zips[i]);
622    free(zips);
623    free(headers);
624
625    if (unmount_when_done != NULL) {
626        ensure_path_unmounted(unmount_when_done);
627    }
628    return result;
629}
630
631static void
632wipe_data(int confirm, Device* device) {
633    if (confirm) {
634        static const char** title_headers = NULL;
635
636        if (title_headers == NULL) {
637            const char* headers[] = { "Confirm wipe of all user data?",
638                                      "  THIS CAN NOT BE UNDONE.",
639                                      "",
640                                      NULL };
641            title_headers = prepend_title((const char**)headers);
642        }
643
644        const char* items[] = { " No",
645                                " No",
646                                " No",
647                                " No",
648                                " No",
649                                " No",
650                                " No",
651                                " Yes -- delete all user data",   // [7]
652                                " No",
653                                " No",
654                                " No",
655                                NULL };
656
657        int chosen_item = get_menu_selection(title_headers, items, 1, 0, device);
658        if (chosen_item != 7) {
659            return;
660        }
661    }
662
663    ui->Print("\n-- Wiping data...\n");
664    device->WipeData();
665    erase_volume("/data");
666    erase_volume("/cache");
667    ui->Print("Data wipe complete.\n");
668}
669
670static void
671prompt_and_wait(Device* device) {
672    const char* const* headers = prepend_title(device->GetMenuHeaders());
673
674    for (;;) {
675        finish_recovery(NULL);
676        ui->SetBackground(RecoveryUI::NO_COMMAND);
677        ui->SetProgressType(RecoveryUI::EMPTY);
678
679        int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device);
680
681        // device-specific code may take some action here.  It may
682        // return one of the core actions handled in the switch
683        // statement below.
684        chosen_item = device->InvokeMenuItem(chosen_item);
685
686        int status;
687        int wipe_cache;
688        switch (chosen_item) {
689            case Device::REBOOT:
690                return;
691
692            case Device::WIPE_DATA:
693                wipe_data(ui->IsTextVisible(), device);
694                if (!ui->IsTextVisible()) return;
695                break;
696
697            case Device::WIPE_CACHE:
698                ui->ShowText(false);
699                ui->Print("\n-- Wiping cache...\n");
700                erase_volume("/cache");
701                ui->Print("Cache wipe complete.\n");
702                if (!ui->IsTextVisible()) return;
703                break;
704
705            case Device::APPLY_EXT:
706                // Some packages expect /cache to be mounted (eg,
707                // standard incremental packages expect to use /cache
708                // as scratch space).
709                ensure_path_mounted(CACHE_ROOT);
710                status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device);
711                if (status == INSTALL_SUCCESS && wipe_cache) {
712                    ui->Print("\n-- Wiping cache (at package request)...\n");
713                    if (erase_volume("/cache")) {
714                        ui->Print("Cache wipe failed.\n");
715                    } else {
716                        ui->Print("Cache wipe complete.\n");
717                    }
718                }
719                if (status >= 0) {
720                    if (status != INSTALL_SUCCESS) {
721                        ui->SetBackground(RecoveryUI::ERROR);
722                        ui->Print("Installation aborted.\n");
723                    } else if (!ui->IsTextVisible()) {
724                        return;  // reboot if logs aren't visible
725                    } else {
726                        ui->Print("\nInstall from sdcard complete.\n");
727                    }
728                }
729                break;
730
731            case Device::APPLY_CACHE:
732                // Don't unmount cache at the end of this.
733                status = update_directory(CACHE_ROOT, NULL, &wipe_cache, device);
734                if (status == INSTALL_SUCCESS && wipe_cache) {
735                    ui->Print("\n-- Wiping cache (at package request)...\n");
736                    if (erase_volume("/cache")) {
737                        ui->Print("Cache wipe failed.\n");
738                    } else {
739                        ui->Print("Cache wipe complete.\n");
740                    }
741                }
742                if (status >= 0) {
743                    if (status != INSTALL_SUCCESS) {
744                        ui->SetBackground(RecoveryUI::ERROR);
745                        ui->Print("Installation aborted.\n");
746                    } else if (!ui->IsTextVisible()) {
747                        return;  // reboot if logs aren't visible
748                    } else {
749                        ui->Print("\nInstall from cache complete.\n");
750                    }
751                }
752                break;
753
754            case Device::APPLY_ADB_SIDELOAD:
755                ensure_path_mounted(CACHE_ROOT);
756                status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
757                if (status >= 0) {
758                    if (status != INSTALL_SUCCESS) {
759                        ui->SetBackground(RecoveryUI::ERROR);
760                        ui->Print("Installation aborted.\n");
761                    } else if (!ui->IsTextVisible()) {
762                        return;  // reboot if logs aren't visible
763                    } else {
764                        ui->Print("\nInstall from ADB complete.\n");
765                    }
766                }
767                break;
768        }
769    }
770}
771
772static void
773print_property(const char *key, const char *name, void *cookie) {
774    printf("%s=%s\n", key, name);
775}
776
777static void
778load_locale_from_cache() {
779    FILE* fp = fopen_path(LOCALE_FILE, "r");
780    char buffer[80];
781    if (fp != NULL) {
782        fgets(buffer, sizeof(buffer), fp);
783        int j = 0;
784        int i;
785        for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) {
786            if (!isspace(buffer[i])) {
787                buffer[j++] = buffer[i];
788            }
789        }
790        buffer[j] = 0;
791        locale = strdup(buffer);
792    }
793}
794
795int
796main(int argc, char **argv) {
797    time_t start = time(NULL);
798
799    // If these fail, there's not really anywhere to complain...
800    freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
801    freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
802
803    // If this binary is started with the single argument "--adbd",
804    // instead of being the normal recovery binary, it turns into kind
805    // of a stripped-down version of adbd that only supports the
806    // 'sideload' command.  Note this must be a real argument, not
807    // anything in the command file or bootloader control block; the
808    // only way recovery should be run with this argument is when it
809    // starts a copy of itself from the apply_from_adb() function.
810    if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
811        adb_main();
812        return 0;
813    }
814
815    printf("Starting recovery on %s", ctime(&start));
816
817    load_volume_table();
818    get_args(&argc, &argv);
819
820    int previous_runs = 0;
821    const char *send_intent = NULL;
822    const char *update_package = NULL;
823    int wipe_data = 0, wipe_cache = 0, show_text = 0;
824    bool just_exit = false;
825
826    int arg;
827    while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
828        switch (arg) {
829        case 'p': previous_runs = atoi(optarg); break;
830        case 's': send_intent = optarg; break;
831        case 'u': update_package = optarg; break;
832        case 'w': wipe_data = wipe_cache = 1; break;
833        case 'c': wipe_cache = 1; break;
834        case 't': show_text = 1; break;
835        case 'x': just_exit = true; break;
836        case 'l': locale = optarg; break;
837        case '?':
838            LOGE("Invalid command argument\n");
839            continue;
840        }
841    }
842
843    if (locale == NULL) {
844        load_locale_from_cache();
845    }
846    printf("locale is [%s]\n", locale);
847
848    Device* device = make_device();
849    ui = device->GetUI();
850
851    ui->Init();
852    ui->SetBackground(RecoveryUI::NONE);
853    if (show_text) ui->ShowText(true);
854
855#ifdef HAVE_SELINUX
856    struct selinux_opt seopts[] = {
857      { SELABEL_OPT_PATH, "/file_contexts" }
858    };
859
860    sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
861
862    if (!sehandle) {
863        fprintf(stderr, "Warning: No file_contexts\n");
864        ui->Print("Warning:  No file_contexts\n");
865    }
866#endif
867
868    device->StartRecovery();
869
870    printf("Command:");
871    for (arg = 0; arg < argc; arg++) {
872        printf(" \"%s\"", argv[arg]);
873    }
874    printf("\n");
875
876    if (update_package) {
877        // For backwards compatibility on the cache partition only, if
878        // we're given an old 'root' path "CACHE:foo", change it to
879        // "/cache/foo".
880        if (strncmp(update_package, "CACHE:", 6) == 0) {
881            int len = strlen(update_package) + 10;
882            char* modified_path = (char*)malloc(len);
883            strlcpy(modified_path, "/cache/", len);
884            strlcat(modified_path, update_package+6, len);
885            printf("(replacing path \"%s\" with \"%s\")\n",
886                   update_package, modified_path);
887            update_package = modified_path;
888        }
889    }
890    printf("\n");
891
892    property_list(print_property, NULL);
893    printf("\n");
894
895    int status = INSTALL_SUCCESS;
896
897    if (update_package != NULL) {
898        status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);
899        if (status == INSTALL_SUCCESS && wipe_cache) {
900            if (erase_volume("/cache")) {
901                LOGE("Cache wipe (requested by package) failed.");
902            }
903        }
904        if (status != INSTALL_SUCCESS) ui->Print("Installation aborted.\n");
905    } else if (wipe_data) {
906        if (device->WipeData()) status = INSTALL_ERROR;
907        if (erase_volume("/data")) status = INSTALL_ERROR;
908        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
909        if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
910    } else if (wipe_cache) {
911        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
912        if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n");
913    } else if (!just_exit) {
914        status = INSTALL_NONE;  // No command specified
915        ui->SetBackground(RecoveryUI::NO_COMMAND);
916    }
917
918    if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
919        ui->SetBackground(RecoveryUI::ERROR);
920    }
921    if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
922        prompt_and_wait(device);
923    }
924
925    // Otherwise, get ready to boot the main system...
926    finish_recovery(send_intent);
927    ui->Print("Rebooting...\n");
928    android_reboot(ANDROID_RB_RESTART, 0, 0);
929    return EXIT_SUCCESS;
930}
931