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/reboot.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <dirent.h>
32 #include <sys/time.h>
33
34 #include "bootloader.h"
35 #include "common.h"
36 //#include "cutils/properties.h"
37 #include "install.h"
38 #include "minui/minui.h"
39 #include "minzip/DirUtil.h"
40 #include "roots.h"
41 #include "recovery_ui.h"
42 #include "encryptedfs_provisioning.h"
43 #include "rktools.h"
44 #include "sdboot.h"
45 #include "usbboot.h"
46 #include "mtdutils/mtdutils.h"
47 #include "recovery_version.h"
48
49 static const char *recovery_version = RECOVERY_VERSION_STRING;
50
51 static const struct option OPTIONS[] = {
52 { "send_intent", required_argument, NULL, 's' },
53 { "update_package", required_argument, NULL, 'u' },
54 { "wipe_data", no_argument, NULL, 'w' },
55 { "wipe_all", no_argument, NULL, 'a' },
56 { "set_encrypted_filesystems", required_argument, NULL, 'e' },
57 { "show_text", no_argument, NULL, 't' },
58 { "factory_pcba_test", no_argument, NULL, 'f' },
59 { "rkdebug", no_argument, NULL, 'r'},
60 { "ui_rotation", required_argument, NULL, 'i'},
61 { NULL, 0, NULL, 0 },
62 };
63
64 static const char *COMMAND_FILE = "/userdata/recovery/command";
65 static const char *INTENT_FILE = "/userdata/recovery/intent";
66 static const char *LOG_FILE = "/userdata/recovery/log";
67 static const char *LAST_LOG_FILE = "/userdata/recovery/last_log";
68 static const char *SDCARD_ROOT = "/sdcard";
69 static const char *SDCARD_ROOT2 = "/mnt/external_sd";
70 static const char *USERDATA_ROOT = "/userdata";
71 static const char *UDISK_ROOT = "/udisk";
72 static const char *UDISK_ROOT2 = "/mnt/usb_storage";
73 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
74 static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
75 static const char *coldboot_done = "/dev/.coldboot_done";
76
77 char systemFlag[252];
78 bool bSDBootUpdate = false;
79 bool bUdiskUpdate = false;
80
81 /*
82 * The recovery tool communicates with the main system through /cache files.
83 * /cache/recovery/command - INPUT - command line for tool, one arg per line
84 * /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
85 * /cache/recovery/intent - OUTPUT - intent that was passed in
86 *
87 * The arguments which may be supplied in the recovery.command file:
88 * --send_intent=anystring - write the text out to recovery.intent
89 * --update_package=path - verify install an OTA package file
90 * --wipe_data - erase user data (and cache), then reboot
91 * --wipe_cache - wipe cache (but not user data), then reboot
92 * --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
93 *
94 * After completing, we remove /cache/recovery/command and reboot.
95 * Arguments may also be supplied in the bootloader control block (BCB).
96 * These important scenarios must be safely restartable at any point:
97 *
98 * FACTORY RESET
99 * 1. user selects "factory reset"
100 * 2. main system writes "--wipe_data" to /cache/recovery/command
101 * 3. main system reboots into recovery
102 * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
103 * -- after this, rebooting will restart the erase --
104 * 5. erase_volume() reformats /userdata
105 * 6. erase_volume() reformats /cache
106 * 7. finish_recovery() erases BCB
107 * -- after this, rebooting will restart the main system --
108 * 8. main() calls reboot() to boot main system
109 *
110 * OTA INSTALL
111 * 1. main system downloads OTA package to /cache/some-filename.zip
112 * 2. main system writes "--update_package=/cache/some-filename.zip"
113 * 3. main system reboots into recovery
114 * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
115 * -- after this, rebooting will attempt to reinstall the update --
116 * 5. install_package() attempts to install the update
117 * NOTE: the package install must itself be restartable from any point
118 * 6. finish_recovery() erases BCB
119 * -- after this, rebooting will (try to) restart the main system --
120 * 7. ** if install failed **
121 * 7a. prompt_and_wait() shows an error icon and waits for the user
122 * 7b; the user reboots (pulling the battery, etc) into the main system
123 * 8. main() calls maybe_install_firmware_update()
124 * ** if the update contained radio/hboot firmware **:
125 * 8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
126 * -- after this, rebooting will reformat cache & restart main system --
127 * 8b. m_i_f_u() writes firmware image into raw cache partition
128 * 8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
129 * -- after this, rebooting will attempt to reinstall firmware --
130 * 8d. bootloader tries to flash firmware
131 * 8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
132 * -- after this, rebooting will reformat cache & restart main system --
133 * 8f. erase_volume() reformats /cache
134 * 8g. finish_recovery() erases BCB
135 * -- after this, rebooting will (try to) restart the main system --
136 * 9. main() calls reboot() to boot main system
137 *
138 * SECURE FILE SYSTEMS ENABLE/DISABLE
139 * 1. user selects "enable encrypted file systems"
140 * 2. main system writes "--set_encrypted_filesystems=on|off" to
141 * /cache/recovery/command
142 * 3. main system reboots into recovery
143 * 4. get_args() writes BCB with "boot-recovery" and
144 * "--set_encrypted_filesystems=on|off"
145 * -- after this, rebooting will restart the transition --
146 * 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /userdata
147 * Settings include: property to specify the Encrypted FS istatus and
148 * FS encryption key if enabled (not yet implemented)
149 * 6. erase_volume() reformats /userdata
150 * 7. erase_volume() reformats /cache
151 * 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /userdata
152 * Settings include: property to specify the Encrypted FS status and
153 * FS encryption key if enabled (not yet implemented)
154 * 9. finish_recovery() erases BCB
155 * -- after this, rebooting will restart the main system --
156 * 10. main() calls reboot() to boot main system
157 */
158
159 static const int MAX_ARG_LENGTH = 4096;
160 static const int MAX_ARGS = 100;
161 extern size_t strlcpy(char *dst, const char *src, size_t dsize);
162 extern size_t strlcat(char *dst, const char *src, size_t dsize);
163 extern int do_rk_updateEngine(const char *binary, const char *path);
164
165
read_encrypted_fs_info(encrypted_fs_info * encrypted_fs_data)166 int read_encrypted_fs_info(encrypted_fs_info *encrypted_fs_data)
167 {
168 return ENCRYPTED_FS_ERROR;
169 }
170
restore_encrypted_fs_info(encrypted_fs_info * encrypted_fs_data)171 int restore_encrypted_fs_info(encrypted_fs_info *encrypted_fs_data)
172 {
173 return ENCRYPTED_FS_ERROR;
174 }
175 // open a given path, mounting partitions as necessary
176 static FILE*
fopen_path(const char * path,const char * mode)177 fopen_path(const char *path, const char *mode)
178 {
179 if (ensure_path_mounted(path) != 0) {
180 LOGE("Can't mount %s\n", path);
181 return NULL;
182 }
183
184 // When writing, try to create the containing directory, if necessary.
185 // Use generous permissions, the system (init.rc) will reset them.
186 if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1);
187
188 FILE *fp = fopen(path, mode);
189 return fp;
190 }
191
192 // close a file, log an error if the error indicator is set
193 static void
check_and_fclose(FILE * fp,const char * name)194 check_and_fclose(FILE *fp, const char *name)
195 {
196 fflush(fp);
197 if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno));
198 fclose(fp);
199 }
200
201 // rockchip partition check (e.g: oem/userdata....)
202 static void
rockchip_partition_check()203 rockchip_partition_check()
204 {
205 if (ensure_path_unmounted("/oem") != 0)
206 LOGE("\n === umount oem fail === \n");
207
208 if (ensure_path_unmounted("/userdata") != 0)
209 LOGE("\n === umount userdata fail === \n");
210
211 ui_print("check userdata/oem partition success ...\n");
212 LOGI("check userdata/oem partition success ...\n");
213 }
214
215 // command line args come from, in decreasing precedence:
216 // - the actual command line
217 // - the bootloader control block (one per line, after "recovery")
218 // - the contents of COMMAND_FILE (one per line)
219 static void
get_args(int * argc,char *** argv)220 get_args(int *argc, char ***argv)
221 {
222 struct bootloader_message boot;
223 memset(&boot, 0, sizeof(boot));
224 get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
225
226 if (boot.command[0] != 0 && boot.command[0] != 255) {
227 LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
228 }
229
230 if (boot.status[0] != 0 && boot.status[0] != 255) {
231 LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
232 }
233
234 // --- if arguments weren't supplied, look in the bootloader control block
235 if (*argc <= 1) {
236 boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
237 const char *arg = strtok(boot.recovery, "\n");
238 if (arg != NULL && !strcmp(arg, "recovery")) {
239 *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
240 (*argv)[0] = strdup(arg);
241 for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
242 if ((arg = strtok(NULL, "\n")) == NULL) break;
243 (*argv)[*argc] = strdup(arg);
244 }
245 LOGI("Got arguments from boot message\n");
246 } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
247 LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
248 }
249 }
250
251 // --- if that doesn't work, try the command file
252 if (*argc <= 1) {
253 FILE *fp = fopen_path(COMMAND_FILE, "r");
254 if (fp != NULL) {
255 char *argv0 = (*argv)[0];
256 *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
257 (*argv)[0] = argv0; // use the same program name
258
259 char buf[MAX_ARG_LENGTH];
260 for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
261 if (!fgets(buf, sizeof(buf), fp)) break;
262 (*argv)[*argc] = strdup(strtok(buf, "\r\n")); // Strip newline.
263 }
264
265 check_and_fclose(fp, COMMAND_FILE);
266 LOGI("Got arguments from %s\n", COMMAND_FILE);
267 }
268 }
269
270 // --> write the arguments we have back into the bootloader control block
271 // always boot into recovery after this (until finish_recovery() is called)
272 strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
273 strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
274 int i;
275 for (i = 1; i < *argc; ++i) {
276 strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
277 strlcat(boot.recovery, "\n", sizeof(boot.recovery));
278 }
279 set_bootloader_message(&boot);
280 }
281
282 static void
set_sdcard_update_bootloader_message()283 set_sdcard_update_bootloader_message()
284 {
285 struct bootloader_message boot;
286 memset(&boot, 0, sizeof(boot));
287 strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
288 strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
289 set_bootloader_message(&boot);
290 }
291
292 // How much of the temp log we have copied to the copy in cache.
293 static long tmplog_offset = 0;
294
295 static void
copy_log_file(const char * destination,int append)296 copy_log_file(const char* destination, int append)
297 {
298 FILE *log = fopen_path(destination, append ? "a" : "w");
299 if (log == NULL) {
300 LOGE("Can't open %s\n", destination);
301 } else {
302 FILE *tmplog = fopen(TEMPORARY_LOG_FILE, "r");
303 if (tmplog == NULL) {
304 LOGE("Can't open %s\n", TEMPORARY_LOG_FILE);
305 } else {
306 if (append) {
307 fseek(tmplog, tmplog_offset, SEEK_SET); // Since last write
308 }
309 char buf[4096];
310 while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
311 if (append) {
312 tmplog_offset = ftell(tmplog);
313 }
314 check_and_fclose(tmplog, TEMPORARY_LOG_FILE);
315 }
316 check_and_fclose(log, destination);
317 }
318 }
319
320
321 // clear the recovery command and prepare to boot a (hopefully working) system,
322 // copy our log file to cache as well (for the system to read), and
323 // record any intent we were asked to communicate back to the system.
324 // this function is idempotent: call it as many times as you like.
325 static void
finish_recovery(const char * send_intent)326 finish_recovery(const char *send_intent)
327 {
328 // By this point, we're ready to return to the main system...
329 if (send_intent != NULL) {
330 FILE *fp = fopen_path(INTENT_FILE, "w");
331 if (fp == NULL) {
332 LOGE("Can't open %s\n", INTENT_FILE);
333 } else {
334 fputs(send_intent, fp);
335 check_and_fclose(fp, INTENT_FILE);
336 }
337 }
338
339 LOGI("finish_recovery Enter.....\n");
340
341 // Copy logs to cache so the system can find out what happened.
342 copy_log_file(LOG_FILE, true);
343 copy_log_file(LAST_LOG_FILE, false);
344 chmod(LAST_LOG_FILE, 0640);
345
346 // Reset to mormal system boot so recovery won't cycle indefinitely.
347 struct bootloader_message boot;
348 memset(&boot, 0, sizeof(boot));
349 strlcpy(boot.systemFlag, systemFlag, sizeof(boot.systemFlag));
350 set_bootloader_message(&boot);
351
352 // Remove the command file, so recovery won't repeat indefinitely.
353 if (ensure_path_mounted(COMMAND_FILE) != 0 ||
354 (unlink(COMMAND_FILE) && errno != ENOENT)) {
355 LOGW("Can't unlink %s\n", COMMAND_FILE);
356 }
357
358 sync(); // For good measure.
359 }
360
361 static int
erase_volume(const char * volume)362 erase_volume(const char *volume)
363 {
364 ui_set_background(BACKGROUND_ICON_INSTALLING);
365 ui_show_indeterminate_progress();
366 ui_print("Formatting %s...\n", volume);
367
368 if (strcmp(volume, "/userdata") == 0) {
369 // Any part of the log we'd copied to data is now gone.
370 // Reset the pointer so we copy from the beginning of the temp
371 // log.
372 tmplog_offset = 0;
373 }
374
375 return format_volume(volume);
376 }
377
378 static char*
copy_sideloaded_package(const char * original_path)379 copy_sideloaded_package(const char* original_path)
380 {
381 if (ensure_path_mounted(original_path) != 0) {
382 LOGE("Can't mount %s\n", original_path);
383 return NULL;
384 }
385
386 if (ensure_path_mounted(SIDELOAD_TEMP_DIR) != 0) {
387 LOGE("Can't mount %s\n", SIDELOAD_TEMP_DIR);
388 return NULL;
389 }
390
391 if (mkdir(SIDELOAD_TEMP_DIR, 0700) != 0) {
392 if (errno != EEXIST) {
393 LOGE("Can't mkdir %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
394 return NULL;
395 }
396 }
397
398 // verify that SIDELOAD_TEMP_DIR is exactly what we expect: a
399 // directory, owned by root, readable and writable only by root.
400 struct stat st;
401 if (stat(SIDELOAD_TEMP_DIR, &st) != 0) {
402 LOGE("failed to stat %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
403 return NULL;
404 }
405 if (!S_ISDIR(st.st_mode)) {
406 LOGE("%s isn't a directory\n", SIDELOAD_TEMP_DIR);
407 return NULL;
408 }
409 if ((st.st_mode & 0777) != 0700) {
410 LOGE("%s has perms %o\n", SIDELOAD_TEMP_DIR, st.st_mode);
411 return NULL;
412 }
413 if (st.st_uid != 0) {
414 LOGE("%s owned by %u; not root\n", SIDELOAD_TEMP_DIR, st.st_uid);
415 return NULL;
416 }
417
418 char copy_path[PATH_MAX];
419 strcpy(copy_path, SIDELOAD_TEMP_DIR);
420 strcat(copy_path, "/package.zip");
421
422 char* buffer = malloc(BUFSIZ);
423 if (buffer == NULL) {
424 LOGE("Failed to allocate buffer\n");
425 return NULL;
426 }
427
428 size_t read;
429 FILE* fin = fopen(original_path, "rb");
430 if (fin == NULL) {
431 LOGE("Failed to open %s (%s)\n", original_path, strerror(errno));
432 return NULL;
433 }
434 FILE* fout = fopen(copy_path, "wb");
435 if (fout == NULL) {
436 LOGE("Failed to open %s (%s)\n", copy_path, strerror(errno));
437 return NULL;
438 }
439
440 while ((read = fread(buffer, 1, BUFSIZ, fin)) > 0) {
441 if (fwrite(buffer, 1, read, fout) != read) {
442 LOGE("Short write of %s (%s)\n", copy_path, strerror(errno));
443 return NULL;
444 }
445 }
446
447 free(buffer);
448
449 if (fclose(fout) != 0) {
450 LOGE("Failed to close %s (%s)\n", copy_path, strerror(errno));
451 return NULL;
452 }
453
454 if (fclose(fin) != 0) {
455 LOGE("Failed to close %s (%s)\n", original_path, strerror(errno));
456 return NULL;
457 }
458
459 // "adb push" is happy to overwrite read-only files when it's
460 // running as root, but we'll try anyway.
461 if (chmod(copy_path, 0400) != 0) {
462 LOGE("Failed to chmod %s (%s)\n", copy_path, strerror(errno));
463 return NULL;
464 }
465
466 return strdup(copy_path);
467 }
468
469 static char**
prepend_title(const char ** headers)470 prepend_title(const char** headers)
471 {
472 char* title[] = { "Linux system recovery <"
473 EXPAND(RECOVERY_API_VERSION) "e>",
474 "",
475 NULL
476 };
477
478 // count the number of lines in our title, plus the
479 // caller-provided headers.
480 int count = 0;
481 char** p;
482 for (p = title; *p; ++p, ++count);
483 for (p = (char** )headers; *p; ++p, ++count);
484
485 char** new_headers = malloc((count + 1) * sizeof(char*));
486 char** h = new_headers;
487 for (p = title; *p; ++p, ++h) *h = *p;
488 for (p = (char** )headers; *p; ++p, ++h) *h = *p;
489 *h = NULL;
490
491 return new_headers;
492 }
493
494 static int
get_menu_selection(char ** headers,char ** items,int menu_only,int initial_selection)495 get_menu_selection(char** headers, char** items, int menu_only,
496 int initial_selection)
497 {
498 // throw away keys pressed previously, so user doesn't
499 // accidentally trigger menu items.
500 ui_clear_key_queue();
501
502 ui_start_menu(headers, items, initial_selection);
503 int selected = initial_selection;
504 int chosen_item = -1;
505
506 while (chosen_item < 0) {
507 int key = ui_wait_key();
508 int visible = ui_text_visible();
509
510 int action = device_handle_key(key, visible);
511
512 if (action < 0) {
513 switch (action) {
514 case HIGHLIGHT_UP:
515 --selected;
516 selected = ui_menu_select(selected);
517 break;
518 case HIGHLIGHT_DOWN:
519 ++selected;
520 selected = ui_menu_select(selected);
521 break;
522 case SELECT_ITEM:
523 chosen_item = selected;
524 break;
525 case NO_ACTION:
526 break;
527 }
528 } else if (!menu_only) {
529 chosen_item = action;
530 }
531 }
532
533 ui_end_menu();
534 return chosen_item;
535 }
536
compare_string(const void * a,const void * b)537 static int compare_string(const void* a, const void* b)
538 {
539 return strcmp(*(const char**)a, *(const char**)b);
540 }
541
542 static int
sdcard_directory(const char * path)543 sdcard_directory(const char* path)
544 {
545 ensure_path_mounted(SDCARD_ROOT);
546
547 const char* MENU_HEADERS[] = { "Choose a package to install:",
548 path,
549 "",
550 NULL
551 };
552 DIR* d;
553 struct dirent* de;
554 d = opendir(path);
555 if (d == NULL) {
556 LOGE("error opening %s: %s\n", path, strerror(errno));
557 ensure_path_unmounted(SDCARD_ROOT);
558 return 0;
559 }
560
561 char** headers = prepend_title(MENU_HEADERS);
562
563 int d_size = 0;
564 int d_alloc = 10;
565 char** dirs = malloc(d_alloc * sizeof(char*));
566 int z_size = 1;
567 int z_alloc = 10;
568 char** zips = malloc(z_alloc * sizeof(char*));
569 zips[0] = strdup("../");
570
571 while ((de = readdir(d)) != NULL) {
572 int name_len = strlen(de->d_name);
573
574 if (de->d_type == DT_DIR) {
575 // skip "." and ".." entries
576 if (name_len == 1 && de->d_name[0] == '.') continue;
577 if (name_len == 2 && de->d_name[0] == '.' &&
578 de->d_name[1] == '.') continue;
579
580 if (d_size >= d_alloc) {
581 d_alloc *= 2;
582 dirs = realloc(dirs, d_alloc * sizeof(char*));
583 }
584 dirs[d_size] = malloc(name_len + 2);
585 strcpy(dirs[d_size], de->d_name);
586 dirs[d_size][name_len] = '/';
587 dirs[d_size][name_len + 1] = '\0';
588 ++d_size;
589 } else if (de->d_type == DT_REG &&
590 name_len >= 4 &&
591 strncasecmp(de->d_name + (name_len - 4), ".zip", 4) == 0) {
592 if (z_size >= z_alloc) {
593 z_alloc *= 2;
594 zips = realloc(zips, z_alloc * sizeof(char*));
595 }
596 zips[z_size++] = strdup(de->d_name);
597 }
598 }
599 closedir(d);
600
601 qsort(dirs, d_size, sizeof(char*), compare_string);
602 qsort(zips, z_size, sizeof(char*), compare_string);
603
604 // append dirs to the zips list
605 if (d_size + z_size + 1 > z_alloc) {
606 z_alloc = d_size + z_size + 1;
607 zips = realloc(zips, z_alloc * sizeof(char*));
608 }
609 memcpy(zips + z_size, dirs, d_size * sizeof(char*));
610 free(dirs);
611 z_size += d_size;
612 zips[z_size] = NULL;
613
614 int result;
615 int chosen_item = 0;
616 do {
617 chosen_item = get_menu_selection(headers, zips, 1, chosen_item);
618
619 char* item = zips[chosen_item];
620 int item_len = strlen(item);
621 if (chosen_item == 0) { // item 0 is always "../"
622 // go up but continue browsing (if the caller is sdcard_directory)
623 result = -1;
624 break;
625 } else if (item[item_len - 1] == '/') {
626 // recurse down into a subdirectory
627 char new_path[PATH_MAX];
628 strlcpy(new_path, path, PATH_MAX);
629 strlcat(new_path, "/", PATH_MAX);
630 strlcat(new_path, item, PATH_MAX);
631 new_path[strlen(new_path) - 1] = '\0'; // truncate the trailing '/'
632 result = sdcard_directory(new_path);
633 if (result >= 0) break;
634 } else {
635 // selected a zip file: attempt to install it, and return
636 // the status to the caller.
637 char new_path[PATH_MAX];
638 strlcpy(new_path, path, PATH_MAX);
639 strlcat(new_path, "/", PATH_MAX);
640 strlcat(new_path, item, PATH_MAX);
641
642 ui_print("\n-- Install %s ...\n", path);
643 set_sdcard_update_bootloader_message();
644 char* copy = copy_sideloaded_package(new_path);
645 ensure_path_unmounted(SDCARD_ROOT);
646 if (copy) {
647 //result = install_package(copy);
648 free(copy);
649 } else {
650 result = INSTALL_ERROR;
651 }
652 break;
653 }
654 } while (true);
655
656 int i;
657 for (i = 0; i < z_size; ++i) free(zips[i]);
658 free(zips);
659 free(headers);
660
661 ensure_path_unmounted(SDCARD_ROOT);
662 return result;
663 }
664
665 static void
wipe_data(int confirm)666 wipe_data(int confirm)
667 {
668 if (confirm) {
669 static char** title_headers = NULL;
670
671 if (title_headers == NULL) {
672 char* headers[] = { "Confirm wipe of all user data?",
673 " THIS CAN NOT BE UNDONE.",
674 "",
675 NULL
676 };
677 title_headers = prepend_title((const char**)headers);
678 }
679
680 char* items[] = { " No",
681 " No",
682 " No",
683 " No",
684 " No",
685 " No",
686 " No",
687 " Yes -- delete all user data", // [7]
688 " No",
689 " No",
690 " No",
691 NULL
692 };
693
694 int chosen_item = get_menu_selection(title_headers, items, 1, 0);
695 if (chosen_item != 7) {
696 return;
697 }
698 }
699
700 ui_print("\n-- Wiping data...\n");
701 device_wipe_data();
702 erase_volume("/userdata");
703 ui_print("Data wipe complete.\n");
704 }
705
ui_update(const char * fw_package)706 static int ui_update(const char * fw_package)
707 {
708 const char* binary = "/usr/bin/rkupdate";
709 int i, ret = 0;
710 int status = INSTALL_SUCCESS;
711
712 for (i = 0; i < 5; i++) {
713 ret = ensure_path_mounted(fw_package);
714 if (ret == 0)
715 break;
716 sleep(1);
717 LOGD("mounted %s failed.\n", fw_package);
718 }
719
720 if (ret == 0 && access(fw_package, F_OK) == 0) {
721 LOGD(">>>rkflash will update from %s\n", fw_package);
722 #ifdef USE_RKUPDATE
723 status = do_rk_update(binary, fw_package);
724 #endif
725
726 #ifdef USE_UPDATEENGINE
727 const char* updateEnginebin = "/usr/bin/updateEngine";
728 status = do_rk_updateEngine(updateEnginebin, fw_package);
729 #endif
730 if (status == INSTALL_SUCCESS) {
731 strcpy(systemFlag, fw_package);
732 /* update success, delete update.img */
733 if (access(fw_package, F_OK) == 0)
734 remove(fw_package);
735 }
736 } else {
737 status = INSTALL_ERROR;
738 }
739
740 if (status != INSTALL_SUCCESS) {
741 ui_print("Installation aborted.\n");
742 while (1) {
743 /* code */
744 }
745
746 return -1;
747 }
748 ui_print("update.img Installation done.\n");
749 ui_show_text(0);
750
751 return 0;
752 }
753
754 static void
prompt_and_wait()755 prompt_and_wait()
756 {
757 char** headers = prepend_title((const char**)MENU_HEADERS);
758
759 for (;;) {
760 finish_recovery(NULL);
761 ui_reset_progress();
762
763 int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0);
764
765 // device-specific code may take some action here. It may
766 // return one of the core actions handled in the switch
767 // statement below.
768 chosen_item = device_perform_action(chosen_item);
769
770 switch (chosen_item) {
771 case ITEM_REBOOT:
772 return;
773
774 case ITEM_WIPE_DATA:
775 wipe_data(ui_text_visible());
776 if (!ui_text_visible()) return;
777 break;
778
779 case ITEM_APPLY_SDCARD: {
780 int status = sdcard_directory(SDCARD_ROOT);
781 if (status >= 0) {
782 if (status != INSTALL_SUCCESS) {
783 ui_set_background(BACKGROUND_ICON_ERROR);
784 ui_print("Installation aborted.\n");
785 } else if (!ui_text_visible()) {
786 return; // reboot if logs aren't visible
787 } else {
788 ui_print("\nInstall from sdcard complete.\n");
789 }
790 }
791 }
792 break;
793
794 case ITEM_APPLY_USERDATA: {
795 //update firmware from local userdata;
796 const char* fw_package = "/userdata/update.img";
797 // LOGD("%s:%d:-------->>>>> USERDATA update\n",__func__, __LINE__);
798 int status = ui_update(fw_package);
799 if (status < 0) {
800 ui_set_background(BACKGROUND_ICON_ERROR);
801 } else if (!ui_text_visible()) {
802 return; // reboot if logs aren't visible
803 } else {
804 ui_print("\nInstall from sdcard complete.\n");
805 }
806 }
807 break;
808
809 case ITEM_APPLY_UDISK: {
810 //update firmware from udisk;
811 const char* fw_package = "/udisk/update.img";
812 // LOGD("%s:%d:-------->>>> UDISK update\n",__func__, __LINE__);
813 int status = ui_update(fw_package);
814 if (status < 0) {
815 ui_set_background(BACKGROUND_ICON_ERROR);
816 } else if (!ui_text_visible()) {
817 ui_print("\nInstall from sdcard complete.\n");
818 return; // reboot if logs aren't visible
819 } else {
820 ui_print("\nInstall from sdcard complete.\n");
821 }
822 }
823 break;
824 }
825 }
826 }
827
828 static void
print_property(const char * key,const char * name,void * cookie)829 print_property(const char *key, const char *name, void *cookie)
830 {
831 printf("%s=%s\n", key, name);
832 }
833
834
835 int
main(int argc,char ** argv)836 main(int argc, char **argv)
837 {
838 bool bSDBoot = false;
839 bool bUDiskBoot = false;
840 const char *sdupdate_package = NULL;
841 const char *usbupdate_package = NULL;
842 int previous_runs = 0;
843 const char *send_intent = NULL;
844 const char *update_package = NULL;
845 const char *encrypted_fs_mode = NULL;
846 int wipe_data = 0;
847 int wipe_all = 0;
848 int pcba_test = 0; // add for pcba test
849 int toggle_secure_fs = 0;
850 int arg;
851 bool isrkdebug = false;
852 int log_level = LOG_DEBUG;
853 encrypted_fs_info encrypted_fs_data;
854 struct timeval start_time, end_time;
855 long long elapsed_time;
856
857 gettimeofday(&start_time, NULL);
858
859 get_args(&argc, &argv);
860 strcpy(systemFlag, "false");
861 while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
862 switch (arg) {
863 case 'p':
864 previous_runs = atoi(optarg);
865 break;
866 case 's':
867 send_intent = optarg;
868 break;
869 case 'u':
870 update_package = optarg;
871 break;
872 case 'w':
873 wipe_data = 1;
874 break;
875 case 'a':
876 wipe_all = 1;
877 break;
878 case 'e':
879 encrypted_fs_mode = optarg;
880 toggle_secure_fs = 1;
881 break;
882 case 't':
883 ui_show_text(1);
884 break;
885 case 'f':
886 pcba_test = 1;
887 break; // add for pcba test
888 case 'r':
889 isrkdebug = true;
890 break;
891 case 'i':
892 gr_set_rotate(atoi(optarg));
893 break;
894 case '?':
895 LOGE("Invalid command argument\n");
896 continue;
897 }
898 }
899
900 time_t start = time(NULL);
901 if ((access("/.rkdebug", F_OK) != 0) && (isrkdebug != true)) {
902 // If these fail, there's not really anywhere to complain...
903 if (freopen(TEMPORARY_LOG_FILE, "a", stdout) == NULL) {
904 LOGW("freopen stdout error");
905 }
906 setbuf(stdout, NULL);
907 if (freopen(TEMPORARY_LOG_FILE, "a", stderr) == NULL) {
908 LOGE("freopen stderr error");
909 }
910 setbuf(stderr, NULL);
911 }
912
913 printf("\n");
914 printf("*********************************************************\n");
915 printf(" ROCKCHIP recovery system \n");
916 printf("*********************************************************\n");
917 printf("**** version : %s ****\n", recovery_version);
918
919 LOGI("Starting recovery on %s\n", ctime(&start));
920 while (access(coldboot_done, F_OK) != 0) {
921 LOGI("coldboot not done, wait...\n");
922 sleep(1);
923 }
924
925 #ifndef RecoveryNoUi
926 LOGI("Recovery System have UI defined.\n");
927 #endif
928
929 ui_init();
930 ui_set_background(BACKGROUND_ICON_INSTALLING);
931 load_volume_table();
932 setFlashPoint();
933
934 bSDBoot = is_boot_from_SD();
935 bUDiskBoot = is_boot_from_udisk();
936
937 if (bSDBoot || bUDiskBoot) {
938 char imageFile[64] = {0};
939 if (bSDBoot) {
940 if (is_sdcard_update()) {
941 strlcpy(imageFile, EX_SDCARD_ROOT, sizeof(imageFile));
942 strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));
943 if (access(imageFile, F_OK) == 0) {
944 sdupdate_package = strdup(imageFile);
945 bSDBootUpdate = true;
946 ui_show_text(1);
947 LOGI("sdupdate_package = %s\n", sdupdate_package);
948 }
949 }
950 }
951
952 if (bUDiskBoot) {
953 if (is_udisk_update()) {
954 strlcpy(imageFile, EX_UDISK_ROOT, sizeof(imageFile));
955 strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));
956 if (access(imageFile, F_OK) == 0) {
957 usbupdate_package = strdup(imageFile);
958 bUdiskUpdate = true;
959 ui_show_text(1);
960 LOGI("usbupdate_package = %s\n", usbupdate_package);
961 }
962 }
963 }
964 }
965
966 device_recovery_start();
967 system("echo default-on > /sys/class/leds/work/trigger");
968
969 LOGI("Command:");
970 for (arg = 0; arg < argc; arg++) {
971 printf(" \"%s\"", argv[arg]);
972 }
973 printf("\n");
974
975 if (update_package) {
976 // For backwards compatibility on the cache partition only, if
977 // we're given an old 'root' path "CACHE:foo", change it to
978 // "/cache/foo".
979 if (strncmp(update_package, "CACHE:", 6) == 0) {
980 int len = strlen(update_package) + 10;
981 char* modified_path = malloc(len);
982 strlcpy(modified_path, "/cache/", len);
983 strlcat(modified_path, update_package + 6, len);
984 LOGI("(replacing path \"%s\" with \"%s\")\n",
985 update_package, modified_path);
986 update_package = modified_path;
987 }
988 }
989 printf("\n");
990
991 int status = INSTALL_SUCCESS;
992
993 if (toggle_secure_fs) {
994 if (strcmp(encrypted_fs_mode, "on") == 0) {
995 encrypted_fs_data.mode = MODE_ENCRYPTED_FS_ENABLED;
996 ui_print("Enabling Encrypted FS.\n");
997 } else if (strcmp(encrypted_fs_mode, "off") == 0) {
998 encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
999 ui_print("Disabling Encrypted FS.\n");
1000 } else {
1001 ui_print("Error: invalid Encrypted FS setting.\n");
1002 status = INSTALL_ERROR;
1003 }
1004
1005 // Recovery strategy: if the data partition is damaged, disable encrypted file systems.
1006 // This preventsthe device recycling endlessly in recovery mode.
1007 if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
1008 (read_encrypted_fs_info(&encrypted_fs_data))) {
1009 ui_print("Encrypted FS change aborted, resetting to disabled state.\n");
1010 encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
1011 }
1012
1013 if (status != INSTALL_ERROR) {
1014 if (erase_volume("/userdata")) {
1015 ui_print("Data wipe failed.\n");
1016 status = INSTALL_ERROR;
1017 #if 0
1018 } else if (erase_volume("/cache")) {
1019 ui_print("Cache wipe failed.\n");
1020 status = INSTALL_ERROR;
1021 #endif
1022 } else if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
1023 (restore_encrypted_fs_info(&encrypted_fs_data))) {
1024 ui_print("Encrypted FS change aborted.\n");
1025 status = INSTALL_ERROR;
1026 } else {
1027 ui_print("Successfully updated Encrypted FS.\n");
1028 status = INSTALL_SUCCESS;
1029 }
1030 }
1031 } else if (update_package != NULL) {
1032 int i, ret = 0;
1033 const char* binary = "/usr/bin/rkupdate";
1034
1035 rockchip_partition_check();
1036
1037 for (i = 0; i < 5; i++) {
1038 if (!ensure_path_mounted(update_package)) {
1039 LOGI("mounted %s Success.\n", update_package);
1040 break;
1041 }
1042 LOGW("mounted %s Failed. retry %d\n", update_package, i + 1);
1043 sleep(1);
1044 }
1045 if (i != 5) {
1046 LOGI(">>>rkflash will update from %s\n", update_package);
1047 #ifdef USE_RKUPDATE
1048 status = do_rk_update(binary, update_package);
1049 #endif
1050 #ifdef USE_UPDATEENGINE
1051 const char* updateEnginebin = "/usr/bin/updateEngine";
1052 status = do_rk_updateEngine(updateEnginebin, update_package);
1053 #endif
1054 if (status == INSTALL_SUCCESS) {
1055 strcpy(systemFlag, update_package);
1056 /* update success, delete update.img. */
1057 if (access(update_package, F_OK) == 0)
1058 remove(update_package);
1059 ui_print("update.img images success!\n");
1060 } else {
1061 ui_print("update.img images failed!\n");
1062 }
1063 } else {
1064 LOGE("mounted %s Failed.\n", update_package);
1065 ui_print("mounted %s Failed.\n", update_package);
1066 }
1067
1068 if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
1069 ui_print("update.img Installation done.\n");
1070 //ui_show_text(0);
1071 } else if (sdupdate_package != NULL) {
1072 rockchip_partition_check();
1073
1074 // update image from sdcard
1075 #ifdef USE_RKUPDATE
1076 const char* binary = "/usr/bin/rkupdate";
1077 LOGI(">>>sdboot update will update from %s\n", sdupdate_package);
1078 status = do_rk_update(binary, sdupdate_package);
1079 #endif
1080
1081 #ifdef USE_UPDATEENGINE
1082 #undef FACTORY_FIRMWARE_IMAGE
1083 #undef CMD4RECOVERY_FILENAME
1084 #define FACTORY_FIRMWARE_IMAGE "/mnt/sdcard/out_image.img"
1085 #define CMD4RECOVERY_FILENAME "/mnt/sdcard/cmd4recovery"
1086 if ((access(FACTORY_FIRMWARE_IMAGE, F_OK)) && access(CMD4RECOVERY_FILENAME, F_OK)) {
1087 int tmp_fd = creat(CMD4RECOVERY_FILENAME, 0777);
1088 if (tmp_fd < 0) {
1089 LOGE("creat %s error.\n", CMD4RECOVERY_FILENAME);
1090 status = INSTALL_ERROR;
1091 } else {
1092 close(tmp_fd);
1093 const char* updateEnginebin = "/usr/bin/updateEngine";
1094 status = do_rk_updateEngine(updateEnginebin, sdupdate_package);
1095 }
1096 }
1097
1098 if (isMtdDevice()) {
1099 LOGI("start flash write to /dev/mtd0.\n");
1100 size_t total_size;
1101 size_t erase_size;
1102 mtd_scan_partitions();
1103 const MtdPartition *part = mtd_find_partition_by_name("rk-nand");
1104 if ( part == NULL ) {
1105 part = mtd_find_partition_by_name("spi-nand0");
1106 }
1107 if (part == NULL || mtd_partition_info(part, &total_size, &erase_size, NULL)) {
1108 if ((!access(FACTORY_FIRMWARE_IMAGE, F_OK)) && mtd_find_partition_by_name("sfc_nor") != NULL) {
1109 LOGI("Info: start flash out_image.img to spi nor.\n");
1110 system("flashcp -v " FACTORY_FIRMWARE_IMAGE " /dev/mtd0");
1111 } else
1112 LOGE("Error: Can't find rk-nand or spi-nand0.\n");
1113 } else {
1114 system("flash_erase /dev/mtd0 0x0 0");
1115 system("sh "CMD4RECOVERY_FILENAME);
1116 }
1117 } else {
1118 LOGI("Start to dd data to emmc partition.\n");
1119 system("sh "CMD4RECOVERY_FILENAME);
1120 LOGI("sdcard upgrade done\n");
1121 }
1122
1123 #endif
1124
1125 if (status == INSTALL_SUCCESS) {
1126 LOGI("update.img Installation success.\n");
1127 ui_print("update.img Installation success.\n");
1128 //ui_show_text(0);
1129 }
1130
1131 } else if (usbupdate_package != NULL) {
1132 rockchip_partition_check();
1133 // update image from udisk
1134 #ifdef USE_RKUPDATE
1135 const char* binary = "/usr/bin/rkupdate";
1136 LOGI(">>>sdboot update will update from %s\n", usbupdate_package);
1137 status = do_rk_update(binary, usbupdate_package);
1138 #endif
1139
1140 #ifdef USE_UPDATEENGINE
1141 #undef FACTORY_FIRMWARE_IMAGE
1142 #undef CMD4RECOVERY_FILENAME
1143 #define FACTORY_FIRMWARE_IMAGE "/mnt/usb_storage/out_image.img"
1144 #define CMD4RECOVERY_FILENAME "/mnt/usb_storage/cmd4recovery"
1145 if ((access(FACTORY_FIRMWARE_IMAGE, F_OK)) && access(CMD4RECOVERY_FILENAME, F_OK)) {
1146 int tmp_fd = creat(CMD4RECOVERY_FILENAME, 0777);
1147 if (tmp_fd < 0) {
1148 LOGE("creat %s error.\n", CMD4RECOVERY_FILENAME);
1149 status = INSTALL_ERROR;
1150 } else {
1151 close(tmp_fd);
1152 const char* updateEnginebin = "/usr/bin/updateEngine";
1153 status = do_rk_updateEngine(updateEnginebin, usbupdate_package);
1154 }
1155 }
1156
1157 if (isMtdDevice()) {
1158 LOGI("start flash write to /dev/mtd0.\n");
1159 size_t total_size;
1160 size_t erase_size;
1161 mtd_scan_partitions();
1162 const MtdPartition *part = mtd_find_partition_by_name("rk-nand");
1163 if ( part == NULL ) {
1164 part = mtd_find_partition_by_name("spi-nand0");
1165 }
1166 if (part == NULL || mtd_partition_info(part, &total_size, &erase_size, NULL)) {
1167 if ((!access(FACTORY_FIRMWARE_IMAGE, F_OK)) && mtd_find_partition_by_name("sfc_nor") != NULL) {
1168 LOGI("Info: start flash out_image.img to spi nor.\n");
1169 system("flashcp -v " FACTORY_FIRMWARE_IMAGE " /dev/mtd0");
1170 } else
1171 LOGE("Error: Can't find rk-nand or spi-nand0.\n");
1172 } else {
1173 system("flash_erase /dev/mtd0 0x0 0");
1174 system("sh "CMD4RECOVERY_FILENAME);
1175 }
1176 } else {
1177 LOGI("Start to dd data to emmc partition.\n");
1178 system("sh "CMD4RECOVERY_FILENAME);
1179 LOGI("usb upgrade done\n");
1180 }
1181 #endif
1182
1183 if (status == INSTALL_SUCCESS) {
1184 LOGI("update.img Installation success.\n");
1185 ui_print("update.img Installation success.\n");
1186 //ui_show_text(0);
1187 }
1188 } else if (wipe_data) {
1189 if (device_wipe_data()) status = INSTALL_ERROR;
1190 // if (erase_volume("/userdata")) status = INSTALL_ERROR;
1191 if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
1192 } else if (wipe_all) {
1193 if (device_wipe_data()) status = INSTALL_ERROR;
1194 // if (erase_volume("/userdata")) status = INSTALL_ERROR;
1195 if (status != INSTALL_SUCCESS) {
1196 ui_print("Data wipe failed.\n");
1197 LOGE("userdata wipe failed.\n");
1198 } else {
1199 ui_print("Data wipe done.\n");
1200 LOGI("userdata wipe done.\n");
1201 }
1202
1203 //ui_show_text(0);
1204 } else if (pcba_test) {
1205 //pcba test todo...
1206 printf("------------------ pcba test start -------------\n");
1207 exit(EXIT_SUCCESS); //exit recovery bin directly, not start pcba here, in rkLanuch.sh
1208 return 0;
1209 } else {
1210 if (argc == 1) { // No command specified
1211 if (!bSDBootUpdate && !bUdiskUpdate && ui_text_visible())
1212 prompt_and_wait();
1213 finish_recovery(NULL);
1214 reboot(RB_AUTOBOOT);
1215 return 0;
1216 }
1217 status = INSTALL_ERROR; // No command specified
1218 }
1219
1220 if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
1221 if (status != INSTALL_SUCCESS) {
1222 LOGE("\n Install fail! \n");
1223 if (!bSDBootUpdate && !bUdiskUpdate && ui_text_visible())
1224 prompt_and_wait();
1225 }
1226
1227 if (sdupdate_package != NULL && bSDBootUpdate) {
1228 if (status == INSTALL_SUCCESS) {
1229 char *SDDdevice =
1230 strdup(get_mounted_device_from_path(EX_SDCARD_ROOT));
1231
1232 ensure_ex_path_unmounted(EX_SDCARD_ROOT);
1233 /* Updating is finished here, we must print this message
1234 * in console, it shows user a specific message that
1235 * updating is completely, remove SD CARD and reboot */
1236 fflush(stdout);
1237 freopen("/dev/console", "w", stdout);
1238 LOGI("\nPlease remove SD CARD!!!, wait for reboot.\n");
1239 ui_print("Please remove SD CARD!!!, wait for reboot.");
1240
1241 if (access(SDDdevice, F_OK) == 0){
1242 system("echo heartbeat > /sys/class/leds/work/trigger");
1243 printf("{ \"PROGRAM\":\"OK\"} \n");
1244
1245 }
1246
1247 while (access(SDDdevice, F_OK) == 0) { sleep(1); }
1248 free(SDDdevice);
1249 }
1250 } else if (usbupdate_package && bUdiskUpdate) {
1251 if (status == INSTALL_SUCCESS) {
1252 char *udiskDev = strdup(get_mounted_device_from_path(EX_SDCARD_ROOT));
1253 ensure_path_unmounted(EX_UDISK_ROOT);
1254 /* Updating is finished here, we must print this message
1255 * in console, it shows user a specific message that
1256 * updating is completely, remove U-disk and reboot */
1257 fflush(stdout);
1258 freopen("/dev/console", "w", stdout);
1259 LOGI("\nPlease remove U DISK!!!, wait for reboot.\n");
1260 ui_print("Please remove U DISK!!!, wait for reboot.");
1261
1262 while (access(udiskDev, F_OK) == 0) { sleep(1); }
1263 free(udiskDev);
1264 }
1265 }
1266
1267 // Otherwise, get ready to boot the main system...
1268 finish_recovery(send_intent);
1269 gettimeofday(&end_time, NULL);
1270
1271 elapsed_time = (end_time.tv_sec - start_time.tv_sec) * 1000LL +
1272 (end_time.tv_usec - start_time.tv_usec) / 1000LL;
1273 LOGI("recovery usage time:%lld ms\n", elapsed_time);
1274 ui_print("Rebooting...\n");
1275 LOGI("Reboot...\n");
1276 ui_show_text(0);
1277 fflush(stdout);
1278 sync();
1279 reboot(RB_AUTOBOOT);
1280 return EXIT_SUCCESS;
1281 }
1282