// 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) { LOGE("Can't open %s\n", INTENT_FILE); } else { 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) { LOGE("Can't open %s\n", LOG_FILE); } else { 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); } #ifndef BOARD_HAS_NO_MISC_PARTITION // Reset to mormal system boot so recovery won't cycle indefinitely. struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); set_bootloader_message(&boot); #endif // 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. }
// 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) { if (*argc > 1) return; // actual command line arguments take priority char *argv0 = (*argv)[0]; struct bootloader_message boot; if (!get_bootloader_message(&boot)) { 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); } // Ensure that from here on, a reboot goes back into recovery strcpy(boot.command, "boot-recovery"); set_bootloader_message(&boot); 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] = argv0; 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"); return; } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) { LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery); } } FILE *fp = fopen_root_path(COMMAND_FILE, "r"); if (fp == NULL) return; *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 && fgets(buf, sizeof(buf), fp); ++*argc) { (*argv)[*argc] = strdup(strtok(buf, "\r\n")); // Strip newline. } check_and_fclose(fp, COMMAND_FILE); LOGI("Got arguments from %s\n", COMMAND_FILE); }
static void get_cmd_file_args(int *argc, char ***argv) { 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_LINE_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); } }
// 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)); #ifndef BOARD_HAS_NO_MISC_PARTITION get_bootloader_message(&boot); // this may fail, leaving a zeroed structure #endif 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); } struct stat file_info; // --- if arguments weren't supplied, look in the bootloader control block if (*argc <= 1 && 0 != stat("/tmp/.ignorebootmessage", &file_info)) { 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)); } #ifndef BOARD_HAS_NO_MISC_PARTITION set_bootloader_message(&boot); #endif }