first_stage_console: Refactor RunScript()

Introduce SpawnImage() as a reusable single-argument wrapper around
posix_spawn(), to avoid having to manually manage the child process.

Note that Bionic currently doesn't return the errno from the child's
exec() call to the caller in the parent process, which may temporarily
hide errors such as ENOENT in first_stage_console until Bionic improves.

Also, this introduces a subtle change in behavior as the first_stage.sh
script is now passed directly to the loader, which will only properly
invoke the Shell if the file contains the right shebang.

Inline the call to RunScript() to hopefully make it simpler for readers
to track the lifetime of the various processes on different code paths.

Test: run first_stage_init
Change-Id: Ifaab2be032b2080a039209295d0b5a3759764ea7
This commit is contained in:
Pierre-Clément Tosi 2024-02-12 17:13:54 +00:00
parent b1d92c6508
commit b6b2afb6b3

View file

@ -16,6 +16,7 @@
#include "first_stage_console.h"
#include <spawn.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
@ -65,19 +66,20 @@ static bool SetupConsole() {
return true;
}
static void RunScript() {
LOG(INFO) << "Attempting to run /first_stage.sh...";
pid_t pid = fork();
if (pid != 0) {
wait(NULL);
LOG(INFO) << "/first_stage.sh exited";
return;
}
const char* path = "/system/bin/sh";
const char* args[] = {path, "/first_stage.sh", nullptr};
int rv = execv(path, const_cast<char**>(args));
LOG(ERROR) << "unable to execv /first_stage.sh, returned " << rv << " errno " << errno;
_exit(127);
static pid_t SpawnImage(const char* file) {
const char* argv[] = {file, NULL};
const char* envp[] = {NULL};
char* const* argvp = const_cast<char* const*>(argv);
char* const* envpp = const_cast<char* const*>(envp);
pid_t pid;
errno = posix_spawn(&pid, argv[0], NULL, NULL, argvp, envpp);
if (!errno) return pid;
PLOG(ERROR) << "Failed to spawn '" << file << "'";
return (pid_t)0;
}
namespace android {
@ -99,12 +101,15 @@ void StartConsole(const std::string& cmdline) {
}
if (console) console = SetupConsole();
RunScript();
LOG(INFO) << "Attempting to run /first_stage.sh...";
if (SpawnImage("/first_stage.sh")) {
wait(NULL);
LOG(INFO) << "/first_stage.sh exited";
}
if (console) {
const char* path = "/system/bin/sh";
const char* args[] = {path, nullptr};
int rv = execv(path, const_cast<char**>(args));
LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
if (SpawnImage("/system/bin/sh")) wait(NULL);
}
_exit(127);
}